From 798e26350a6b1b9b7f0e161bd3a8bc9fa9002b34 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Thu, 22 Jun 2023 18:15:14 +0530 Subject: [PATCH 001/146] 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 002/146] 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 003/146] 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 004/146] 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 005/146] 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 006/146] 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 007/146] 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 008/146] 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 009/146] 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 010/146] 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 011/146] 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 012/146] 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 013/146] 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 014/146] 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 015/146] 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 016/146] 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 017/146] 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 018/146] 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 019/146] 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 020/146] 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 021/146] 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 022/146] 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 023/146] 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 024/146] 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 025/146] 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 026/146] 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 027/146] 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 028/146] 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 029/146] 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 030/146] 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 031/146] 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 032/146] 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 033/146] 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 034/146] 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 035/146] 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 036/146] 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 From b62be6a418f5fe916bcc8cfcb7a5711aea8a6bce Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 21 May 2025 18:43:38 +0530 Subject: [PATCH 037/146] fix: filter out comment lines from ssh-keyscan output in verify-fingerprint.sh --- trusted-fingerprint/client/verify-fingerprint.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trusted-fingerprint/client/verify-fingerprint.sh b/trusted-fingerprint/client/verify-fingerprint.sh index f695d93..eb4622a 100755 --- a/trusted-fingerprint/client/verify-fingerprint.sh +++ b/trusted-fingerprint/client/verify-fingerprint.sh @@ -14,7 +14,7 @@ hashed_hostname=$HOST keyscan_output=$(ssh-keyscan -T $KEYSCAN_TIMEOUT -t $USER_KEY_TYPE $HOST) [[ "$hash_known_hosts" == "yes" ]] && hashed_hostname=$(echo "$keyscan_output" | awk '{print $1}') -host_key=$(echo "$keyscan_output" | awk '{print $3}') +host_key=$(echo "$keyscan_output" | grep -v "^#" | awk '{print $3}') # Check if the key is empty if [[ -z "$host_key" ]]; then @@ -50,7 +50,7 @@ else lambda_response_key=$(cat $tempkeyfile) rm $tempkeyfile - if [[ + if [[ $host_key == $lambda_response_key ]]; then From b5c0707aaa7e324913fc9e26a94962efa9c90a27 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 30 May 2025 17:04:56 +0530 Subject: [PATCH 038/146] Improve AWS metadata handling, SSH cert principals, and Docker Alpine version - Add IMDSv2 token to AWS metadata requests (required by newer EC2 instances) - Include host IP address as principal in SSH host certificate - Pin Docker base image to specific Alpine version to resolve compatibility issues - Remove client X.509 certificate generation script --- private-ca/client/Docker/Dockerfile | 6 +- .../Docker/generate-certificate-curl.sh | 37 ++------- .../server/generate-client-x509-cert.js | 79 ------------------- private-ca/server/generate-host-ssh-cert.js | 4 +- private-ca/server/index_lambda.js | 4 +- 5 files changed, 11 insertions(+), 119 deletions(-) delete mode 100644 private-ca/server/generate-client-x509-cert.js diff --git a/private-ca/client/Docker/Dockerfile b/private-ca/client/Docker/Dockerfile index f2bb956..f6e3af0 100644 --- a/private-ca/client/Docker/Dockerfile +++ b/private-ca/client/Docker/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:latest +FROM alpine:3.18.3 RUN apk add bash curl unzip groff jq python3 openssh openssl RUN python3 -m ensurepip @@ -9,7 +9,7 @@ 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 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/generate-certificate-curl.sh b/private-ca/client/Docker/generate-certificate-curl.sh index c4ddf62..7226cf6 100755 --- a/private-ca/client/Docker/generate-certificate-curl.sh +++ b/private-ca/client/Docker/generate-certificate-curl.sh @@ -4,7 +4,6 @@ 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"} AWS_STS_REGION=${6:-"ap-southeast-1"} AWS_PROFILE=${7:-"default"} @@ -18,7 +17,6 @@ while getopts ":h" option; do 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" @@ -64,39 +62,19 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then 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 | tr -d \\n) - -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 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 - 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 | 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 -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) +TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 120") +INSTANCE_ROLE_NAME=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/) +TEMP_CREDS=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$INSTANCE_ROLE_NAME) +PUBLIC_IP=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-ipv4) ACCESS_KEY_ID=$(echo $TEMP_CREDS | jq -r ".AccessKeyId") SECRET_ACCESS_KEY=$(echo $TEMP_CREDS | jq -r ".SecretAccessKey") @@ -107,7 +85,7 @@ output=$($PYTHON_EXEC aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESS 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}\"}") +EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\",\"publicIp\":\"${PUBLIC_IP}\"}") if [[ $CA_ACTION = "generateClientSSHCert" ]]; then LAMBDA_RESPONSE=$(curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON") @@ -140,9 +118,4 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then 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) - sh -c "echo '$CERTIFICATE' > ${SYSTEM_SSL_DIR}/privateCA/public.crt" - echo "Certificate written to ${SYSTEM_SSL_DIR}/privateCA/public.crt" fi diff --git a/private-ca/server/generate-client-x509-cert.js b/private-ca/server/generate-client-x509-cert.js deleted file mode 100644 index 545291f..0000000 --- a/private-ca/server/generate-client-x509-cert.js +++ /dev/null @@ -1,79 +0,0 @@ -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) => { - - 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. 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); - - // openssl genrsa -out key.pem 2048 - // openssl rsa -in key.pem -outform PEM -pubout -out public.pem - const clientPublicKey = pki.publicKeyFromPem(Buffer.from(event.certPubkey, 'base64').toString('utf-8')); - - // Create a client certificate signing request (CSR) - const clientCertReq = pki.createCertificationRequest(); - clientCertReq.publicKey = clientPublicKey; - clientCertReq.setSubject([ - { 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 = 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); - clientCert.setExtensions([ - { name: 'basicConstraints', cA: false }, - { name: 'keyUsage', digitalSignature: true, nonRepudiation: true, keyEncipherment: true }, - ]); - - 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); - return { - statusCode: 200, - body: Buffer.from(clientCertPem).toString('base64') - }; - -} \ No newline at end of file diff --git a/private-ca/server/generate-host-ssh-cert.js b/private-ca/server/generate-host-ssh-cert.js index eedfccd..cf9a9df 100644 --- a/private-ca/server/generate-host-ssh-cert.js +++ b/private-ca/server/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, certPubkey) => { +export const signHostSSHCertificate = async (callerIdentity, secret, certPubkey, publicIp) => { const arn = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn; const roleName = arn.match(/\/([^/]+)$/)?.[1]; @@ -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} -t rsa-sha2-512 -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 ${publicIp} -V +1d ${publicKeyPath}`)); console.log('stdout:', stdout); console.log('stderr:', stderr); diff --git a/private-ca/server/index_lambda.js b/private-ca/server/index_lambda.js index 80af176..d9f7e5a 100644 --- a/private-ca/server/index_lambda.js +++ b/private-ca/server/index_lambda.js @@ -18,7 +18,7 @@ export const handler = async (event) => { // action switch(event.action) { case "generateHostSSHCert": - const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.certPubkey); + const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.certPubkey, event.publicIp); return { statusCode: 200, body: "{\"certificate\" : \""+Buffer.from(hostSSHCert).toString('base64')+"\", \"user_ca.pub\": \""+secret["user_ca.pub"]+"\"}" @@ -29,8 +29,6 @@ export const handler = async (event) => { statusCode: 200, body: "{\"certificate\" : \""+Buffer.from(clientSSHCert).toString('base64')+"\", \"host_ca.pub\": \""+secret["host_ca.pub"]+"\"}" }; - case "generateClientX509Cert": - return await generateClientX509Cert(callerIdentity, secret, event); default: console.log("Invalid Action") return { From de78c369ea7277015711d0c4c90d8deb6dc58357 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 30 May 2025 17:12:25 +0530 Subject: [PATCH 039/146] Update Docker README --- private-ca/client/Docker/README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/private-ca/client/Docker/README.md b/private-ca/client/Docker/README.md index cdc5d1f..135a6eb 100644 --- a/private-ca/client/Docker/README.md +++ b/private-ca/client/Docker/README.md @@ -1,6 +1,6 @@ # 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. +This Docker container runs a script that generates SSH certificates using a Private Certificate Authority (CA) hosted on AWS Lambda. The script provides options to generate SSH certificates for hosts and clients. ## Prerequisites @@ -19,16 +19,16 @@ This Docker container runs a script that generates SSH and X.509 certificates us 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" @@ -36,12 +36,10 @@ This Docker container runs a script that generates SSH and X.509 certificates us 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 From ec42fb13a8b496d7083aa6824b8bba94a1ac225b Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 30 May 2025 17:33:01 +0530 Subject: [PATCH 040/146] fix: remove X.509 certificate generation from client and update README --- private-ca/README.md | 26 ++++++++--------- private-ca/client/Docker/run.sh | 2 +- .../client/generate-certificate-curl.sh | 29 ------------------- 3 files changed, 13 insertions(+), 44 deletions(-) diff --git a/private-ca/README.md b/private-ca/README.md index 0f85d5a..434deab 100644 --- a/private-ca/README.md +++ b/private-ca/README.md @@ -1,43 +1,41 @@ -# Private Certificate Authority (CA) for SSH and SSL Certificates +# Private Certificate Authority (CA) for SSH 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 client SSL certificates for secure communication. +This project provides a private Certificate Authority (CA) implementation for generating SSH certificates. It allows you to issue certificates for SSH hosts and users for secure communication. ## Installation - Deploy the resources by running: - ```bash - ./deploy-server-on-lambda.sh - ``` +```bash +./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 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. +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 Certificates can be generated by running: - ```bash - cd client/Docker - ./generate-certificate-curl.sh - ``` +```bash +cd client/Docker +./generate-certificate-curl.sh +``` You can pass the following params to modify the payload: - `CA_ACTION` - generateHostSSHCert - generateClientSSHCert - - generateClientX509Cert - `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 +- `AWS_PROFILE`: The AWS profile for running aws commands. Defaults to "default" diff --git a/private-ca/client/Docker/run.sh b/private-ca/client/Docker/run.sh index 48261b9..0e33255 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 * * 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 +echo "0 0 */1 * * cd /app && generate-certificate-curl.sh generateHostSSHCert ${CA_LAMBDA_URL} ${USER_SSH_DIR} ${SYSTEM_SSH_DIR} ${AWS_STS_REGION} ${AWS_PROFILE} > /dev/stdout" > crontab.txt /usr/bin/crontab crontab.txt /usr/sbin/crond -f -l 8 diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 3c1d32a..0c9d7be 100755 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -4,7 +4,6 @@ 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"} @@ -18,7 +17,6 @@ while getopts ":h" option; do 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" @@ -64,33 +62,11 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then 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 @@ -143,11 +119,6 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then 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 From c8aa3a1648e76cf076b942ec93ccba08a6c26638 Mon Sep 17 00:00:00 2001 From: fundabot Date: Fri, 30 May 2025 12:09:01 +0000 Subject: [PATCH 041/146] CI: bumps ssm-env-util to 1.0.1-update/privateCA.0 [skip ci] --- ssm-env-util/package-lock.json | 4 ++-- ssm-env-util/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ssm-env-util/package-lock.json b/ssm-env-util/package-lock.json index 6bd0674..fb0020f 100644 --- a/ssm-env-util/package-lock.json +++ b/ssm-env-util/package-lock.json @@ -1,12 +1,12 @@ { "name": "ssm-env-util", - "version": "1.0.0", + "version": "1.0.1-update/privateCA.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ssm-env-util", - "version": "1.0.0", + "version": "1.0.1-update/privateCA.0", "license": "ISC", "dependencies": { "@aws-sdk/client-ssm": "^3.624.0", diff --git a/ssm-env-util/package.json b/ssm-env-util/package.json index c5336aa..0caab26 100644 --- a/ssm-env-util/package.json +++ b/ssm-env-util/package.json @@ -1,6 +1,6 @@ { "name": "ssm-env-util", - "version": "1.0.0", + "version": "1.0.1-update/privateCA.0", "description": "get parameters from ssm by parsing .env.template", "type": "module", "main": "index.js", From d47032ffb66d0537a2f12c31a93e63a33ed357a4 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Mon, 2 Jun 2025 12:14:36 +0530 Subject: [PATCH 042/146] fix: parameter index in generate-certificate scripts --- private-ca/client/Docker/generate-certificate-curl.sh | 4 ++-- private-ca/client/generate-certificate-curl.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/private-ca/client/Docker/generate-certificate-curl.sh b/private-ca/client/Docker/generate-certificate-curl.sh index 7226cf6..3858da5 100755 --- a/private-ca/client/Docker/generate-certificate-curl.sh +++ b/private-ca/client/Docker/generate-certificate-curl.sh @@ -4,8 +4,8 @@ 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"} -AWS_STS_REGION=${6:-"ap-southeast-1"} -AWS_PROFILE=${7:-"default"} +AWS_STS_REGION=${5:-"ap-southeast-1"} +AWS_PROFILE=${6:-"default"} PYTHON_EXEC=$(which python || which python3) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 0c9d7be..307ced6 100755 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -4,8 +4,8 @@ CA_ACTION=${1} CA_LAMBDA_URL=${2} USER_SSH_DIR=${3:-"/home/$USER/.ssh"} SYSTEM_SSH_DIR=${4:-"/etc/ssh"} -AWS_STS_REGION=${6:-"ap-southeast-1"} -AWS_PROFILE=${7:-"default"} +AWS_STS_REGION=${5:-"ap-southeast-1"} +AWS_PROFILE=${6:-"default"} PYTHON_EXEC=$(which python || which python3) From 96c2037b7e4460fb5edfa2997f8b49a20ceea0c0 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Mon, 2 Jun 2025 15:34:18 +0530 Subject: [PATCH 043/146] fix: generate certificate using AWS CLI script - Fixed payload structure as required for Lambda invocation using jq. - Added support for AWS profile, AWS_STS_REGION and SYSTEM_SSH_DIR as parameters. - Updated CA action names - Refactoring --- .../client/generate-certificate-aws-cli.sh | 67 ++++++++++--------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index 2a2a72d..0bb6d29 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -1,17 +1,14 @@ #!/bin/bash - +AWS_PROFILE=${1:-"default"} +AWS_STS_REGION=${2:-"ap-southeast-1"} +SYSTEM_SSH_DIR=${3:-"/etc/ssh"} CA_LAMBDA_FUNCTION_NAME="privateCA" +LAMBDA_REGION=${AWS_REGION:-'us-west-2'} # 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="" +CA_ACTION="generateHostSSHCert" +# CA_ACTION="generateClientSSHCert" # # Get host SSH certificate # SSH_ATTRS_VALIDITY="" @@ -23,44 +20,54 @@ CA_ACTION="generateRootX509Cert" ###################################################### # Temporary Credentials -TEMP_CREDS=$(aws sts get-session-token) +TEMP_CREDS=$(aws sts get-session-token --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") 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) +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") +CERT_PUBKEY=$(cat ${SYSTEM_SSH_DIR}/ssh_host_rsa_key.pub | base64 | tr -d \\n) -echo "{\"auth\": { - \"amzDate\": \"${date}\", - \"authorizationHeader\": \"${auth_header}\", - \"sessionToken\": \"${SESSION_TOKEN}\" - }, - \"sslAttrs\": { - \"validityPeriod\": \"${SSL_ATTRS_VALIDITY}\", - \"clientPublicKeyPem\": \"${SSL_CLIENT_PUBKEY_PEM}\" +INNER_JSON=$(jq -n \ + --arg amzDate "$date" \ + --arg authHeader "$auth_header" \ + --arg sessionToken "$SESSION_TOKEN" \ + --arg certPubkey "$CERT_PUBKEY" \ + --arg action "$CA_ACTION" \ + --arg awsRegion "$AWS_STS_REGION" \ + '{ + auth: { + amzDate: $amzDate, + authorizationHeader: $authHeader, + sessionToken: $sessionToken }, - \"sshAttrs\": { - \"validity\": \"${SSH_ATTRS_VALIDITY}\", - \"sshHostRSAKey\": \"${SSH_HOST_RSA_PUBKEY}\", - \"sshClientRSAKey\": \"${SSH_CLIENT_RSA_PUBKEY}\" - }, - \"action\": \"${CA_ACTION}\" - }" > event.json + certPubkey: $certPubkey, + action: $action, + awsSTSRegion: $awsRegion + }' | jq -c) + +# JSON with body as stringified JSON +final_json=$(jq -n --arg body "$INNER_JSON" '{body: $body}') +echo "$final_json" > 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 \ + --profile $AWS_PROFILE -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') +response_body=$(cat response.json | jq -r ".body" | tr -d '"' | sed 's/\\r\\n/\\n/g') echo ${response_body} From 00612e80b48ff01bebf3ebfbccf00476282181e4 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 3 Jun 2025 15:04:30 +0530 Subject: [PATCH 044/146] feat: update script for cert generation using AWS CLI script to have checks for valid certificates and save generated certs - Updated parameter handling to include USER_SSH_DIR and improved usage instructions. - Added checks for existing SSH certificates and their validity. - Saving generated Certs along with updating the SSH config and known_hosts --- .../client/generate-certificate-aws-cli.sh | 129 ++++++++++++++++-- 1 file changed, 116 insertions(+), 13 deletions(-) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index 0bb6d29..c6d9c8d 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -1,15 +1,14 @@ #!/bin/bash -AWS_PROFILE=${1:-"default"} -AWS_STS_REGION=${2:-"ap-southeast-1"} -SYSTEM_SSH_DIR=${3:-"/etc/ssh"} -CA_LAMBDA_FUNCTION_NAME="privateCA" -LAMBDA_REGION=${AWS_REGION:-'us-west-2'} +CA_ACTION=${1:-"generateHostSSHCert"} +AWS_PROFILE=${2:-"default"} +USER_SSH_DIR=${3:-"/home/$USER/.ssh"} +SYSTEM_SSH_DIR=${4:-"/etc/ssh"} +LAMBDA_REGION=${5:-'us-west-2'} +CA_LAMBDA_FUNCTION_NAME=${6:-"privateCA"} +AWS_STS_REGION=${7:-"ap-southeast-1"} # Edit values here ###################################################### -CA_ACTION="generateHostSSHCert" -# CA_ACTION="generateClientSSHCert" - # # Get host SSH certificate # SSH_ATTRS_VALIDITY="" # SSH_HOST_RSA_PUBKEY="" @@ -19,12 +18,85 @@ CA_ACTION="generateHostSSHCert" # SSH_CLIENT_RSA_PUBKEY="" ###################################################### +# Check for options +while getopts ":h" option; do + case $option in + h) + echo "Usage: bash generate-certificate-aws-cli.sh [CA_ACTION] [AWS_PROFILE] [AWS_STS_REGION] [LAMBDA_REGION] [SYSTEM_SSH_DIR] [CA_LAMBDA_FUNCTION_NAME]" + echo "" + echo "Parameters:" + echo " CA_ACTION Action to perform (default: generateHostSSHCert)" + echo " AWS_PROFILE AWS profile to use (default: default)" + echo " USER_SSH_DIR User SSH directory path (default: /home/$USER/.ssh)" + echo " SYSTEM_SSH_DIR System SSH directory path (default: /etc/ssh)" + echo " LAMBDA_REGION Lambda function region (default: us-west-2)" + echo " CA_LAMBDA_FUNCTION_NAME Lambda function name (default: privateCA)" + echo " AWS_STS_REGION AWS STS region (default: ap-southeast-1)" + echo "" + echo "CA_ACTION:" + echo " generateHostSSHCert Generates SSH Certificate for Host" + echo " generateClientSSHCert Generates SSH 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 > $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 || 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) +else + echo "Invalid Action" + echo "Possible actions include:" + echo " generateHostSSHCert: Generates SSH Certificate for Host" + echo " generateClientSSHCert: Generates SSH Certificate for Client" + exit; +fi + # Temporary Credentials TEMP_CREDS=$(aws sts get-session-token --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") +PUBLIC_IP="" PYTHON_EXEC=$(which python || which python3) @@ -44,6 +116,7 @@ INNER_JSON=$(jq -n \ --arg certPubkey "$CERT_PUBKEY" \ --arg action "$CA_ACTION" \ --arg awsRegion "$AWS_STS_REGION" \ + --arg publicIp "$PUBLIC_IP" \ '{ auth: { amzDate: $amzDate, @@ -52,12 +125,13 @@ INNER_JSON=$(jq -n \ }, certPubkey: $certPubkey, action: $action, - awsSTSRegion: $awsRegion + awsSTSRegion: $awsRegion, + publicIp: $publicIp }' | jq -c) # JSON with body as stringified JSON -final_json=$(jq -n --arg body "$INNER_JSON" '{body: $body}') -echo "$final_json" > event.json +json_body=$(jq -n --arg body "$INNER_JSON" '{body: $body}') +echo "$json_body" > event.json aws lambda invoke \ --function-name ${CA_LAMBDA_FUNCTION_NAME} \ @@ -67,9 +141,38 @@ aws lambda invoke \ --region $LAMBDA_REGION \ --profile $AWS_PROFILE -response_body=$(cat response.json | jq -r ".body" | tr -d '"' | sed 's/\\r\\n/\\n/g') +response_body=$(cat response.json | jq -r ".body") + +if [[ $CA_ACTION = "generateClientSSHCert" ]]; then + ENCODED_CERTIFICATE=$(echo $response_body | jq -r ".certificate") + CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) + HOST_CA_PUBKEY=$(echo $response_body | 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 + ENCODED_CERTIFICATE=$(echo $response_body | jq -r ".certificate") + CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) + USER_CA_PUBKEY=$(echo $response_body | 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 -echo ${response_body} + 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 +fi # Clean up deactivate From be000a0419e1eac8701012c006f74312767cac91 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 3 Jun 2025 16:40:09 +0530 Subject: [PATCH 045/146] fix: use virtual env Python binary in generate-certificate-curl.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Updated script to re-evaluate the Python executable after activating the virtual environment. • Ensures that the script uses the environment where boto3 is installed. --- private-ca/client/generate-certificate-curl.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 307ced6..a391d0d 100755 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -77,10 +77,14 @@ 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 +# Update PYTHON_EXEC to use the Python executable from the activated virtual environment +# This ensures we use the venv's Python with the installed dependencies (boto3) +PYTHON_EXEC=$(which python || which python3) + +# Auth Headers 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") From e1042c98bce19d80b46a67780f8e9e6a8b0af3f2 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 3 Jun 2025 16:50:46 +0530 Subject: [PATCH 046/146] refactor: restructure private-ca client - remove Docker subdirectory - Move all Docker files to parent client directory - Rename Docker/generate-certificate-curl.sh to generate-certificate-curl-ec2.sh --- private-ca/client/{Docker => }/Dockerfile | 0 private-ca/client/{Docker => }/README.md | 0 private-ca/client/{Docker => }/aws-auth-header.py | 0 ...erate-certificate-curl.sh => generate-certificate-curl-ec2.sh} | 0 private-ca/client/{Docker => }/run.sh | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename private-ca/client/{Docker => }/Dockerfile (100%) rename private-ca/client/{Docker => }/README.md (100%) rename private-ca/client/{Docker => }/aws-auth-header.py (100%) rename private-ca/client/{Docker/generate-certificate-curl.sh => generate-certificate-curl-ec2.sh} (100%) rename private-ca/client/{Docker => }/run.sh (100%) diff --git a/private-ca/client/Docker/Dockerfile b/private-ca/client/Dockerfile similarity index 100% rename from private-ca/client/Docker/Dockerfile rename to private-ca/client/Dockerfile diff --git a/private-ca/client/Docker/README.md b/private-ca/client/README.md similarity index 100% rename from private-ca/client/Docker/README.md rename to private-ca/client/README.md diff --git a/private-ca/client/Docker/aws-auth-header.py b/private-ca/client/aws-auth-header.py similarity index 100% rename from private-ca/client/Docker/aws-auth-header.py rename to private-ca/client/aws-auth-header.py diff --git a/private-ca/client/Docker/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl-ec2.sh similarity index 100% rename from private-ca/client/Docker/generate-certificate-curl.sh rename to private-ca/client/generate-certificate-curl-ec2.sh diff --git a/private-ca/client/Docker/run.sh b/private-ca/client/run.sh similarity index 100% rename from private-ca/client/Docker/run.sh rename to private-ca/client/run.sh From efe0fa5f7a4e9a569ce3ee758ac3ab575b9533d9 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 3 Jun 2025 17:44:19 +0530 Subject: [PATCH 047/146] clean up outdated comments --- private-ca/client/generate-certificate-aws-cli.sh | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index c6d9c8d..0aa04e3 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -7,16 +7,6 @@ LAMBDA_REGION=${5:-'us-west-2'} CA_LAMBDA_FUNCTION_NAME=${6:-"privateCA"} AWS_STS_REGION=${7:-"ap-southeast-1"} -# Edit values here -###################################################### -# # Get host SSH certificate -# SSH_ATTRS_VALIDITY="" -# SSH_HOST_RSA_PUBKEY="" - -# # Get client SSH certificate -# SSH_ATTRS_VALIDITY="" -# SSH_CLIENT_RSA_PUBKEY="" -###################################################### # Check for options while getopts ":h" option; do From 65c010093ad6886b00fed18498f3a9a8f66e1fd0 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 4 Jun 2025 16:39:38 +0530 Subject: [PATCH 048/146] feat: add function to get temp AWS creds based on the environment and merge curl and curl-ec2 scripts - Merged generate-certificate-curl.sh and generate-certificate-curl-ec2.sh scripts - Added ENVIRONMENT to params - Implemented checks for USER_SSH_DIR and known_hosts with clear error messaging - Ensured Python virtual environment is reused if it already exists - Improved handling of stdout and stderr for better debugging and logging --- .../client/generate-certificate-curl-ec2.sh | 121 ------------------ .../client/generate-certificate-curl.sh | 73 ++++++++--- 2 files changed, 52 insertions(+), 142 deletions(-) delete mode 100755 private-ca/client/generate-certificate-curl-ec2.sh mode change 100755 => 100644 private-ca/client/generate-certificate-curl.sh diff --git a/private-ca/client/generate-certificate-curl-ec2.sh b/private-ca/client/generate-certificate-curl-ec2.sh deleted file mode 100755 index 3858da5..0000000 --- a/private-ca/client/generate-certificate-curl-ec2.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/bin/bash - -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"} -AWS_STS_REGION=${5:-"ap-southeast-1"} -AWS_PROFILE=${6:-"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" - 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 > $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 || 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) -else - echo "Invalid Action" - echo "Possible actions include:" - echo " generateHostSSHCert: Generates SSH Certificate for Host" - echo " generateClientSSHCert: Generates SSH Certificate for Client" - exit; -fi - -# Temporary Credentials -TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 120") -INSTANCE_ROLE_NAME=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/) -TEMP_CREDS=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$INSTANCE_ROLE_NAME) -PUBLIC_IP=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-ipv4) - -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 -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}\",\"publicIp\":\"${PUBLIC_IP}\"}") - -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) - - 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 -fi diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh old mode 100755 new mode 100644 index a391d0d..2e98062 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -1,13 +1,42 @@ #!/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"} AWS_STS_REGION=${5:-"ap-southeast-1"} AWS_PROFILE=${6:-"default"} - -PYTHON_EXEC=$(which python || which python3) +ENVIRONMENT=${7:-"client"} + +PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) +[[ $? -ne 0 ]] && { echo "Python not installed."; exit 1; } + +get_aws_credentials() { + local method=${1:-"host"} + + if [[ $method == "host" ]]; then + TOKEN=$(curl -s --max-time 30 -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 120") + + if [[ -z "$TOKEN" ]]; then + echo "Failed to fetch EC2 metadata token. Are you running this script on an EC2 instance?" + exit 1 + fi + + INSTANCE_ROLE_NAME=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/) + TEMP_CREDS=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$INSTANCE_ROLE_NAME) + PUBLIC_IP=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-ipv4) + + 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") + + elif [[ $method == "client" ]]; then + TEMP_CREDS=$(aws sts get-session-token --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") + fi +} # Check for options while getopts ":h" option; do @@ -26,6 +55,9 @@ done # Check for CA Action if [[ $CA_ACTION = "generateClientSSHCert" ]]; then + + [[ -d "${USER_SSH_DIR}" ]] || { echo "User SSH directory does not exist. Please provide the correct user SSH directory."; exit 1; } + 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") @@ -50,8 +82,8 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then 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 + 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; @@ -60,7 +92,7 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then 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 "" + 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 | tr -d \\n) else echo "Invalid Action" @@ -70,26 +102,24 @@ else 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") +get_aws_credentials $ENVIRONMENT -$PYTHON_EXEC -m venv env && source env/bin/activate -pip install boto3 +if [ ! -d "env" ]; then + $PYTHON_EXEC -m venv env +fi +source env/bin/activate +pip install -q --upgrade boto3 # Update PYTHON_EXEC to use the Python executable from the activated virtual environment # This ensures we use the venv's Python with the installed dependencies (boto3) -PYTHON_EXEC=$(which python || which python3) +PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) # Auth Headers 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}\"}") +EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\",\"publicIp\":\"${PUBLIC_IP}\"}") if [[ $CA_ACTION = "generateClientSSHCert" ]]; then @@ -101,17 +131,20 @@ 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" + [[ -f "${USER_SSH_DIR}/known_hosts" ]] || touch "${USER_SSH_DIR}/known_hosts" + 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 +# sudo access is required to generate host certificate 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" + 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 @@ -125,6 +158,4 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then fi fi -# Clean up -deactivate -rm -r env/ \ No newline at end of file +deactivate \ No newline at end of file From 89452dc92a0d47cf8f0c45ee956da0fd566a873e Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 4 Jun 2025 17:21:44 +0530 Subject: [PATCH 049/146] refactor: update generate-certificate-curl.sh for improved AWS credential handling - Changed parameter order for better clarity and added USER_AWS_DIR. - Introduced is_mfa_enabled function to check for MFA credentials. - Updated get_aws_credentials function to handle MFA and standard credential retrieval. - Updated CA_LAMBDA_URL to CA_URL --- .../client/generate-certificate-curl.sh | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 2e98062..b0981cc 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -1,16 +1,21 @@ #!/bin/bash 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"} -AWS_STS_REGION=${5:-"ap-southeast-1"} -AWS_PROFILE=${6:-"default"} -ENVIRONMENT=${7:-"client"} +CA_URL=${2:-$CA_URL} +AWS_PROFILE=${3:-"default"} +ENVIRONMENT=${4:-"client"} +USER_SSH_DIR=${5:-"/home/$USER/.ssh"} +USER_AWS_DIR=${6:-"/home/$USER/.aws"} +SYSTEM_SSH_DIR=${7:-"/etc/ssh"} +AWS_STS_REGION=${8:-"ap-southeast-1"} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) [[ $? -ne 0 ]] && { echo "Python not installed."; exit 1; } +is_mfa_enabled() { + grep -q 'get-credentials' ${USER_AWS_DIR}/credentials +} + get_aws_credentials() { local method=${1:-"host"} @@ -31,10 +36,24 @@ get_aws_credentials() { SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".Token") elif [[ $method == "client" ]]; then - TEMP_CREDS=$(aws sts get-session-token --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") + if is_mfa_enabled; then + CALLER_IDENTITY=$(aws sts get-caller-identity --profile $AWS_PROFILE) + [[ $? -ne 0 ]] && { echo "Your AWS credentials have either expired or are invalid. Please check your credentials and try again."; exit 1; } + + TEMP_CREDS=$(get-credentials $AWS_PROFILE) + 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 ".SessionToken") + + else + TEMP_CREDS=$(aws sts get-session-token --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") + fi + else + echo "Invalid environment" + exit 1 fi } @@ -123,7 +142,7 @@ EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\" if [[ $CA_ACTION = "generateClientSSHCert" ]]; then - LAMBDA_RESPONSE=$(curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON") + LAMBDA_RESPONSE=$(curl "${CA_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) @@ -139,7 +158,7 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then # sudo access is required to generate host certificate elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then - LAMBDA_RESPONSE=$(curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON") + LAMBDA_RESPONSE=$(curl "${CA_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) From 712af90cc0e5c9eb27c8ba2d863037a70c58d4c1 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Thu, 5 Jun 2025 16:35:53 +0530 Subject: [PATCH 050/146] update README, Dockerfile, change parameter index - Update README.md with clearer usage instructions and added sections for running directly and via Docker. - Removed the obsolete run.sh script. --- private-ca/client/Dockerfile | 13 +- private-ca/client/README.md | 138 +++++++++++++----- .../client/generate-certificate-curl.sh | 4 +- private-ca/client/run.sh | 4 - 4 files changed, 106 insertions(+), 53 deletions(-) delete mode 100644 private-ca/client/run.sh diff --git a/private-ca/client/Dockerfile b/private-ca/client/Dockerfile index f6e3af0..444cf5a 100644 --- a/private-ca/client/Dockerfile +++ b/private-ca/client/Dockerfile @@ -1,15 +1,12 @@ FROM alpine:3.18.3 RUN apk add bash curl unzip groff jq python3 openssh openssl -RUN python3 -m ensurepip -RUN pip3 install boto3 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 +COPY generate-certificate-curl.sh . +COPY aws-auth-header.py . -ENTRYPOINT ["/app/run.sh" ] +RUN chmod +x generate-certificate-curl.sh + +ENTRYPOINT ["bash", "generate-certificate-curl.sh"] \ No newline at end of file diff --git a/private-ca/client/README.md b/private-ca/client/README.md index 135a6eb..6f59437 100644 --- a/private-ca/client/README.md +++ b/private-ca/client/README.md @@ -1,61 +1,121 @@ -# Certificate Generator Docker Container +# PrivateCA -This Docker container runs a script that generates SSH certificates using a Private Certificate Authority (CA) hosted on AWS Lambda. The script provides options to generate SSH certificates for hosts and clients. +This project provides tools to generate SSH certificates using a Private Certificate Authority (CA). It supports generating SSH certificates for both hosts and clients. ## Prerequisites -- Docker: Install Docker on your system. Refer to the [Docker documentation](https://docs.docker.com/get-docker/) for installation instructions. +### Running via Docker + +- Docker + +### Running directly + +- Python 3 +- Bash +- Dependencies: `curl`, `jq`, `ssh-keygen`, `base64` ## Usage -1. Clone this repository or download the Dockerfile and the `generate-certificate.sh` script. +### Running directly -2. Build the Docker image using the following command: +#### For client certificates: - ```shell - docker build -t generate-certificate . - ``` +```bash +bash generate-certificate-curl.sh generateClientSSHCert client +``` + +#### For host certificates: -3. Run the Docker container with the desired parameters. The container requires specific environment variables to be set: +```bash +bash generate-certificate-curl.sh generateHostSSHCert host +``` - - `CA_ACTION`: Specify the action to perform. Possible values are: +**Note:** - - `generateHostSSHCert`: Generates an SSH certificate for the host. - - `generateClientSSHCert`: Generates an SSH certificate for a client. +1. Sudo privilege is required for generating host certificates as they need to write to system directories like `/etc/ssh`. +2. The `ENVIRONMENT` (host or client) parameter only affects how AWS credentials are retrieved, not what type of certificates you can generate. See [Script Parameters](#script-parameters) for more details. - - `CA_LAMBDA_URL`: The URL of the AWS Lambda function hosting the Private CA. +### Running via Docker - Optional environment variables: +1. Build the Docker image: - - `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". - - `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" + ```bash + docker build -t certificate-generator . + ``` - ```shell - docker run -it --rm \ - -v /path/to/ssh/directory:/home/$USER/.ssh \ - -v /path/to/system/ssh/directory:/etc/ssh \ - -e CA_ACTION= \ - -e CA_LAMBDA_URL= \ - -e USER_SSH_DIR= \ - -e SYSTEM_SSH_DIR= \ - -e AWS_STS_REGION= \ - -e AWS_PROFILE= \ - generate-certificate +2. Run the Docker container with the required volume mounts and parameters: + + ```bash + docker run --rm \ + -v /home/harshit/.ssh:/root/.ssh \ + -v /etc/ssh:/etc/ssh \ + -v /etc/ssl/privateCA:/etc/ssl/privateCA \ + certificate-generator \ + generateHostSSHCert \ + https:/// \ + host \ + default \ + /root/.ssh ``` -4. The script will generate the necessary certificates based on the provided action and store them in the specified directories. +## Running as a cron job (optional) + +Since certificates need to be renewed periodically, you can set up a cron job to automatically regenerate them. + +Sample script: + +```bash +#!/bin/bash + +# Create the cron job entry +echo "* */1 * * * cd bash generate-certificate-curl.sh generateHostSSHCert https:/// host >> /home/cron.log 2>&1" > /tmp/root_crontab + +# Load into root's crontab +crontab -u root /tmp/root_crontab + +# Optionally start cron service (only if not already running) +systemctl start cron 2>/dev/null || systemctl start crond 2>/dev/null +``` + +## Script Parameters + +The `generate-certificate-curl.sh` script accepts the following parameters: + +1. **CA_ACTION** (required): The action to perform + + - `generateClientSSHCert`: Generates an SSH certificate for clients + - `generateHostSSHCert`: Generates an SSH certificate for hosts + +2. **CA_URL** (required): URL of the Private CA + +3. **ENVIRONMENT** (optional): Machine environment type - "client" or "host" (defaults to "client") + + - **"host"**: For EC2 instances - uses EC2 instance metadata for AWS credentials + - **"client"**: For local user machines - uses AWS CLI/STS for credentials + - _Note: This parameter only affects how AWS credentials are retrieved, not what type of certificates you can generate_ + +4. **AWS_PROFILE** (optional): AWS profile name (defaults to "default") + +5. **USER_SSH_DIR** (optional): Path to user's SSH directory (defaults to "/home/$USER/.ssh") + +6. **USER_AWS_DIR** (optional): Path to user's AWS directory (defaults to "/home/$USER/.aws") + +7. **SYSTEM_SSH_DIR** (optional): Path to system SSH directory (defaults to "/etc/ssh") + +8. **AWS_STS_REGION** (optional): AWS region for STS (defaults to "ap-southeast-1") -## Script Explanation +## Important Notes -The `generate-certificate.sh` script performs the following tasks: +- **Environment vs Certificate Type**: The `ENVIRONMENT` parameter determines how AWS credentials are retrieved, not what certificates you can generate + - You can generate both client and host certificates from either environment type + - EC2 instances should use `ENVIRONMENT=host` for credential retrieval via instance metadata + - Local user machines should use `ENVIRONMENT=client` for credential retrieval via AWS CLI +- **Certificate Type**: Determined by the `CA_ACTION` parameter (`generateClientSSHCert` or `generateHostSSHCert`) +- **Permissions**: Host certificates require sudo privileges for system directory access -- 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. +## Directory Structure -Make sure to adjust the script parameters and environment variables according to your specific requirements. +- `generate-certificate-curl.sh`: Main script for certificate generation using curl +- `generate-certificate-aws-cli.sh`: Alternative script using AWS CLI +- `aws-auth-header.py`: Python helper for generating AWS authentication headers +- `Dockerfile`: Docker container configuration diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index b0981cc..ac52adf 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -2,8 +2,8 @@ CA_ACTION=${1:-$CA_ACTION} CA_URL=${2:-$CA_URL} -AWS_PROFILE=${3:-"default"} -ENVIRONMENT=${4:-"client"} +ENVIRONMENT=${3:-"client"} +AWS_PROFILE=${4:-"default"} USER_SSH_DIR=${5:-"/home/$USER/.ssh"} USER_AWS_DIR=${6:-"/home/$USER/.aws"} SYSTEM_SSH_DIR=${7:-"/etc/ssh"} diff --git a/private-ca/client/run.sh b/private-ca/client/run.sh deleted file mode 100644 index 0e33255..0000000 --- a/private-ca/client/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -echo "0 0 */1 * * cd /app && generate-certificate-curl.sh generateHostSSHCert ${CA_LAMBDA_URL} ${USER_SSH_DIR} ${SYSTEM_SSH_DIR} ${AWS_STS_REGION} ${AWS_PROFILE} > /dev/stdout" > crontab.txt -/usr/bin/crontab crontab.txt -/usr/sbin/crond -f -l 8 From 4f8df0bd429cbff8297324959771c50571843763 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Thu, 5 Jun 2025 16:53:56 +0530 Subject: [PATCH 051/146] update README.md --- private-ca/README.md | 18 +----------------- private-ca/client/README.md | 2 +- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/private-ca/README.md b/private-ca/README.md index 434deab..096810a 100644 --- a/private-ca/README.md +++ b/private-ca/README.md @@ -22,20 +22,4 @@ Note: Once the lambda is deployed you will need to manually add an environment v ## Usage -Certificates can be generated by running: - -```bash -cd client/Docker -./generate-certificate-curl.sh -``` - -You can pass the following params to modify the payload: - -- `CA_ACTION` - - generateHostSSHCert - - generateClientSSHCert -- `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". -- `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" +See [client/README.md](client/README.md) for usage. diff --git a/private-ca/client/README.md b/private-ca/client/README.md index 6f59437..e4cbb66 100644 --- a/private-ca/client/README.md +++ b/private-ca/client/README.md @@ -47,7 +47,7 @@ bash generate-certificate-curl.sh generateHostSSHCert host ```bash docker run --rm \ - -v /home/harshit/.ssh:/root/.ssh \ + -v /home/$USER/.ssh:/root/.ssh \ -v /etc/ssh:/etc/ssh \ -v /etc/ssl/privateCA:/etc/ssl/privateCA \ certificate-generator \ From 234346578d877f3772b31d4a69dbaa0cb9a705b2 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 6 Jun 2025 15:18:47 +0530 Subject: [PATCH 052/146] chore: migrate aws-sdk to v3 in Private CA --- private-ca/server/index_lambda.js | 1 - private-ca/server/package-lock.json | 2387 +++++++++++++++++---- private-ca/server/package.json | 2 +- private-ca/server/secret-manager-utils.js | 12 +- 4 files changed, 1991 insertions(+), 411 deletions(-) diff --git a/private-ca/server/index_lambda.js b/private-ca/server/index_lambda.js index d9f7e5a..3d5179c 100644 --- a/private-ca/server/index_lambda.js +++ b/private-ca/server/index_lambda.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 { generateClientX509Cert } from './generate-client-x509-cert.js'; import { getSecret } from './secret-manager-utils.js'; const AWS_SCRTS_REGION = process.env.AWS_SCRTS_REGION; diff --git a/private-ca/server/package-lock.json b/private-ca/server/package-lock.json index 3600019..4fb39af 100644 --- a/private-ca/server/package-lock.json +++ b/private-ca/server/package-lock.json @@ -9,588 +9,2167 @@ "version": "0.0.1", "license": "ISC", "dependencies": { - "aws-sdk": "^2.1398.0", + "@aws-sdk/client-secrets-manager": "^3.825.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==", + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.4" + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=14.0.0" } }, - "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==", + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", "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" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 10.0.0" + "node": ">=14.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==", + "node_modules/@aws-sdk/client-secrets-manager": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.825.0.tgz", + "integrity": "sha512-cOryB1tID+u9pD2fiyt0wG+o++9UUASS0EdtVND0KW5N4FV/fCKDYmKsH4Lp3fKKEtTVqtD2szQ24Mnx1shYZA==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.825.0", + "@aws-sdk/credential-provider-node": "3.825.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.825.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.825.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.2", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.10", + "@smithy/middleware-retry": "^4.1.11", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.2", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.18", + "@smithy/util-defaults-mode-node": "^4.0.18", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } }, - "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==", + "node_modules/@aws-sdk/client-sso": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.825.0.tgz", + "integrity": "sha512-U0J2RQUsxiin+uEYR8atMByojuvhtWvvEVSD2MhSUUnCa7BSu/H+4SbREBvnGDJ2nezrYh59bkSQBlp9c3Z9gg==", "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.825.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.825.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.825.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.2", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.10", + "@smithy/middleware-retry": "^4.1.11", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.2", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.18", + "@smithy/util-defaults-mode-node": "^4.0.18", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.825.0.tgz", + "integrity": "sha512-UsdK6l62skh6mqY/La4xvehNj5sUl/eZ2N+8mNTHZKW4U+tiRESdrw1t/Z3r/NUAu7Tbmp+DHbUu+5K1BBY6YQ==", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/core": "^3.5.2", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.2", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-utf8": "^4.0.0", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.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==", + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.825.0.tgz", + "integrity": "sha512-Ptkbhj4K1un+GIz5fmTLVCFtWv9rcbaCLgdZszudo/ZqLP0QzAoACADGYFFkPGYr2o51COKkgKPhHWl7FNEq6A==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "@aws-sdk/core": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.825.0.tgz", + "integrity": "sha512-r0V0rlNCjnFLfYfUqP6TlwAo+YgWxIkrgUb/K6mV2XCBElbFZlc9oPzMOJCmHF/+D6S60FLlMC9AnFopnEZ3/A==", + "dependencies": { + "@aws-sdk/core": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.2", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.4.x" + "node": ">=18.0.0" } }, - "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==", + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.825.0.tgz", + "integrity": "sha512-HDYopAiIGTLLhybI8jEuKGWdVUnKkkotwXHwvu8ttL5qgs13A6a/iWiREe71fmYH2fGT2URJE9+xeHa2oxohyQ==", "dependencies": { - "is-callable": "^1.1.3" + "@aws-sdk/core": "3.825.0", + "@aws-sdk/credential-provider-env": "3.825.0", + "@aws-sdk/credential-provider-http": "3.825.0", + "@aws-sdk/credential-provider-process": "3.825.0", + "@aws-sdk/credential-provider-sso": "3.825.0", + "@aws-sdk/credential-provider-web-identity": "3.825.0", + "@aws-sdk/nested-clients": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "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/@aws-sdk/credential-provider-node": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.825.0.tgz", + "integrity": "sha512-qWMrrUgWFQN7nkMdQYzWF/Z/fhUctCjwTQQD/qNSs42qt3sxmC00SZcqwPn9N8S9R/hLmu5z6fefVF4o20nGng==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.825.0", + "@aws-sdk/credential-provider-http": "3.825.0", + "@aws-sdk/credential-provider-ini": "3.825.0", + "@aws-sdk/credential-provider-process": "3.825.0", + "@aws-sdk/credential-provider-sso": "3.825.0", + "@aws-sdk/credential-provider-web-identity": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "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==", + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.825.0.tgz", + "integrity": "sha512-QQoOBQAXuBfD6BCg61Hl5EkdrLyFSQCNRHVLjAO5WYQGyiPb9iTZPqo9sPwyOnCMpZE1k2EOwQ+FsnZh0xSa3Q==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "@aws-sdk/core": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.825.0.tgz", + "integrity": "sha512-ppwsN8tuwwJKvNnllkrhIx7AQv4r5uiNf5FTIkyeJ+3p67wgJeJye+0SP64IEkdmG7YxCaU2YkdSvyHud+D5og==", "dependencies": { - "get-intrinsic": "^1.1.3" + "@aws-sdk/client-sso": "3.825.0", + "@aws-sdk/core": "3.825.0", + "@aws-sdk/token-providers": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.825.0.tgz", + "integrity": "sha512-cyL5xHqtvBUpflkmdQSkvjD/t+Dl/ZSXvPnc9KF79xDpuraZ5tFP1l0B6rIEu7dUzUh8XG+7m2CZ6TEs6QU33Q==", "dependencies": { - "function-bind": "^1.1.1" + "@aws-sdk/core": "3.825.0", + "@aws-sdk/nested-clients": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4.0" + "node": ">=18.0.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==", + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.821.0.tgz", + "integrity": "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.4" + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.821.0.tgz", + "integrity": "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "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==", + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.821.0.tgz", + "integrity": "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.4" + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.825.0.tgz", + "integrity": "sha512-3ZZOPU3GE5cqKl6VFDwiL8KIvlrrQJ4rgYkeiF+m5kA0eXV2xFOwoLgm3AmPB+6kfo9HQ0N74KKJV0teS5nO6Q==", + "dependencies": { + "@aws-sdk/core": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@smithy/core": "^3.5.2", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "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==", + "node_modules/@aws-sdk/nested-clients": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.825.0.tgz", + "integrity": "sha512-OuV2pypFAv52Lty8eXWVWyyOywVmMAsgH6Gq3SA06pHEtcE+ghVIW9ByegecyfMRUpedAiovARKNy0pfGX05Pg==", "dependencies": { - "has-symbols": "^1.0.2" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.825.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.825.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.825.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.2", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.10", + "@smithy/middleware-retry": "^4.1.11", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.2", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.18", + "@smithy/util-defaults-mode-node": "^4.0.18", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.821.0.tgz", + "integrity": "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "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/@aws-sdk/token-providers": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.825.0.tgz", + "integrity": "sha512-a3HbF6h1Gq2vA+mGlxFe3op65wNK6dBRmp3GFwsPVQ+OFTbZJi86FCljMfBrv+BGYUkp503/IPC49wuRHOdcZA==", + "dependencies": { + "@aws-sdk/core": "3.825.0", + "@aws-sdk/nested-clients": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "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/@aws-sdk/types": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.821.0.tgz", + "integrity": "sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA==", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "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==", + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.821.0.tgz", + "integrity": "sha512-Uknt/zUZnLE76zaAAPEayOeF5/4IZ2puTFXvcSCWHsi9m3tqbb9UozlnlVqvCZLCRWfQryZQoG2W4XSS3qgk5A==", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "@smithy/util-endpoints": "^3.0.6", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz", + "integrity": "sha512-zVoRfpmBVPodYlnMjgVjfGoEZagyRF5IPn3Uo6ZvOZp24chnW/FRstH7ESDHDDRga4z3V+ElUQHKpFDXWyBW5A==", + "dependencies": { + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "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==", + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.821.0.tgz", + "integrity": "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.825.0.tgz", + "integrity": "sha512-RfB0w9YJSsFGsbrzOQ1VE2O4NwR6gxelUvmz8PzuerPCg4iD4JW7hSCmnoAEi51Xnq0bNeCsnhzXJzIlPe04jA==", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.4" + "node": ">=18.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "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==", + "node_modules/@aws-sdk/xml-builder": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz", + "integrity": "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==", "dependencies": { - "has-tostringtag": "^1.0.0" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", + "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "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==", + "node_modules/@smithy/config-resolver": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.4.tgz", + "integrity": "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==", "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" + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.5.3.tgz", + "integrity": "sha512-xa5byV9fEguZNofCclv6v9ra0FYh5FATQW/da7FQUVTic94DfrN/NvmKZjrMyzbpqfot9ZjBaO8U1UeTbmSLuA==", + "dependencies": { + "@smithy/middleware-serde": "^4.0.8", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "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/@smithy/credential-provider-imds": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.6.tgz", + "integrity": "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "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==", + "node_modules/@smithy/fetch-http-handler": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.4.tgz", + "integrity": "sha512-AMtBR5pHppYMVD7z7G+OlHHAcgAN7v0kVKEpHuTO4Gb199Gowh0taYi9oDStFeUhetkeP55JLSVlTW1n9rFtUw==", + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.6.0" + "node": ">=18.0.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==", + "node_modules/@smithy/hash-node": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.4.tgz", + "integrity": "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==", + "dependencies": { + "@smithy/types": "^4.3.1", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 6.13.0" + "node": ">=18.0.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/@smithy/invalid-dependency": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.4.tgz", + "integrity": "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "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.", + "node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.4.x" + "node": ">=18.0.0" } }, - "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/@smithy/middleware-content-length": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.4.tgz", + "integrity": "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==", + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "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==", + "node_modules/@smithy/middleware-endpoint": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.11.tgz", + "integrity": "sha512-zDogwtRLzKl58lVS8wPcARevFZNBOOqnmzWWxVe9XiaXU2CADFjvJ9XfNibgkOWs08sxLuSr81NrpY4mgp9OwQ==", "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" + "@smithy/core": "^3.5.3", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.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==", + "node_modules/@smithy/middleware-retry": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.12.tgz", + "integrity": "sha512-wvIH70c4e91NtRxdaLZF+mbLZ/HcC6yg7ySKUiufL6ESp6zJUSnJucZ309AvG9nqCFHSRB5I6T3Ez1Q9wCh0Ww==", "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" + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/service-error-classification": "^4.0.5", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "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==", + "node_modules/@smithy/middleware-serde": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz", + "integrity": "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==", + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.4.tgz", + "integrity": "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.3.tgz", + "integrity": "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==", + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", + "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", + "dependencies": { + "@smithy/abort-controller": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.4.tgz", + "integrity": "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.2.tgz", + "integrity": "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.4.tgz", + "integrity": "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==", + "dependencies": { + "@smithy/types": "^4.3.1", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.4.tgz", + "integrity": "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.5.tgz", + "integrity": "sha512-LvcfhrnCBvCmTee81pRlh1F39yTS/+kYleVeLCwNtkY8wtGg8V/ca9rbZZvYIl8OjlMtL6KIjaiL/lgVqHD2nA==", + "dependencies": { + "@smithy/types": "^4.3.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.4.tgz", + "integrity": "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.2.tgz", + "integrity": "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.3.tgz", + "integrity": "sha512-xxzNYgA0HD6ETCe5QJubsxP0hQH3QK3kbpJz3QrosBCuIWyEXLR/CO5hFb2OeawEKUxMNhz3a1nuJNN2np2RMA==", + "dependencies": { + "@smithy/core": "^3.5.3", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", + "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.4.tgz", + "integrity": "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==", + "dependencies": { + "@smithy/querystring-parser": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", + "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", + "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", + "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", + "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", + "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.19.tgz", + "integrity": "sha512-mvLMh87xSmQrV5XqnUYEPoiFFeEGYeAKIDDKdhE2ahqitm8OHM3aSvhqL6rrK6wm1brIk90JhxDf5lf2hbrLbQ==", + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.19.tgz", + "integrity": "sha512-8tYnx+LUfj6m+zkUUIrIQJxPM1xVxfRBvoGHua7R/i6qAxOMjqR6CpEpDwKoIs1o0+hOjGvkKE23CafKL0vJ9w==", + "dependencies": { + "@smithy/config-resolver": "^4.1.4", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.6.tgz", + "integrity": "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.4.tgz", + "integrity": "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.5.tgz", + "integrity": "sha512-V7MSjVDTlEt/plmOFBn1762Dyu5uqMrV2Pl2X0dYk4XvWfdWJNe9Bs5Bzb56wkCuiWjSfClVMGcsuKrGj7S/yg==", "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" + "@smithy/service-error-classification": "^4.0.5", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 0.4" + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.2.tgz", + "integrity": "sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w==", + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18.0.0" } }, - "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==", + "node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", + "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=4.0.0" + "node": ">=18.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==", + "node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", + "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=4.0" + "node": ">=18.0.0" } + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==" + }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "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/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" } }, "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-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "requires": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "requires": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "requires": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + } + } + } }, - "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==", + "@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", "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" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" } }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + "@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "requires": { + "tslib": "^2.6.2" + } }, - "buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "requires": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "requires": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + } + } + } + }, + "@aws-sdk/client-secrets-manager": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.825.0.tgz", + "integrity": "sha512-cOryB1tID+u9pD2fiyt0wG+o++9UUASS0EdtVND0KW5N4FV/fCKDYmKsH4Lp3fKKEtTVqtD2szQ24Mnx1shYZA==", + "requires": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.825.0", + "@aws-sdk/credential-provider-node": "3.825.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.825.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.825.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.2", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.10", + "@smithy/middleware-retry": "^4.1.11", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.2", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.18", + "@smithy/util-defaults-mode-node": "^4.0.18", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "dependencies": { + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" + } } }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "@aws-sdk/client-sso": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.825.0.tgz", + "integrity": "sha512-U0J2RQUsxiin+uEYR8atMByojuvhtWvvEVSD2MhSUUnCa7BSu/H+4SbREBvnGDJ2nezrYh59bkSQBlp9c3Z9gg==", "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.825.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.825.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.825.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.2", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.10", + "@smithy/middleware-retry": "^4.1.11", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.2", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.18", + "@smithy/util-defaults-mode-node": "^4.0.18", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" } }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==" + "@aws-sdk/core": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.825.0.tgz", + "integrity": "sha512-UsdK6l62skh6mqY/La4xvehNj5sUl/eZ2N+8mNTHZKW4U+tiRESdrw1t/Z3r/NUAu7Tbmp+DHbUu+5K1BBY6YQ==", + "requires": { + "@aws-sdk/types": "3.821.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/core": "^3.5.2", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.2", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-utf8": "^4.0.0", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + } }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "@aws-sdk/credential-provider-env": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.825.0.tgz", + "integrity": "sha512-Ptkbhj4K1un+GIz5fmTLVCFtWv9rcbaCLgdZszudo/ZqLP0QzAoACADGYFFkPGYr2o51COKkgKPhHWl7FNEq6A==", "requires": { - "is-callable": "^1.1.3" + "@aws-sdk/core": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" } }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "@aws-sdk/credential-provider-http": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.825.0.tgz", + "integrity": "sha512-r0V0rlNCjnFLfYfUqP6TlwAo+YgWxIkrgUb/K6mV2XCBElbFZlc9oPzMOJCmHF/+D6S60FLlMC9AnFopnEZ3/A==", + "requires": { + "@aws-sdk/core": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.2", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", + "tslib": "^2.6.2" + } }, - "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==", + "@aws-sdk/credential-provider-ini": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.825.0.tgz", + "integrity": "sha512-HDYopAiIGTLLhybI8jEuKGWdVUnKkkotwXHwvu8ttL5qgs13A6a/iWiREe71fmYH2fGT2URJE9+xeHa2oxohyQ==", "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "@aws-sdk/core": "3.825.0", + "@aws-sdk/credential-provider-env": "3.825.0", + "@aws-sdk/credential-provider-http": "3.825.0", + "@aws-sdk/credential-provider-process": "3.825.0", + "@aws-sdk/credential-provider-sso": "3.825.0", + "@aws-sdk/credential-provider-web-identity": "3.825.0", + "@aws-sdk/nested-clients": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" } }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "@aws-sdk/credential-provider-node": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.825.0.tgz", + "integrity": "sha512-qWMrrUgWFQN7nkMdQYzWF/Z/fhUctCjwTQQD/qNSs42qt3sxmC00SZcqwPn9N8S9R/hLmu5z6fefVF4o20nGng==", "requires": { - "get-intrinsic": "^1.1.3" + "@aws-sdk/credential-provider-env": "3.825.0", + "@aws-sdk/credential-provider-http": "3.825.0", + "@aws-sdk/credential-provider-ini": "3.825.0", + "@aws-sdk/credential-provider-process": "3.825.0", + "@aws-sdk/credential-provider-sso": "3.825.0", + "@aws-sdk/credential-provider-web-identity": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "@aws-sdk/credential-provider-process": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.825.0.tgz", + "integrity": "sha512-QQoOBQAXuBfD6BCg61Hl5EkdrLyFSQCNRHVLjAO5WYQGyiPb9iTZPqo9sPwyOnCMpZE1k2EOwQ+FsnZh0xSa3Q==", "requires": { - "function-bind": "^1.1.1" + "@aws-sdk/core": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" } }, - "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==" + "@aws-sdk/credential-provider-sso": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.825.0.tgz", + "integrity": "sha512-ppwsN8tuwwJKvNnllkrhIx7AQv4r5uiNf5FTIkyeJ+3p67wgJeJye+0SP64IEkdmG7YxCaU2YkdSvyHud+D5og==", + "requires": { + "@aws-sdk/client-sso": "3.825.0", + "@aws-sdk/core": "3.825.0", + "@aws-sdk/token-providers": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + "@aws-sdk/credential-provider-web-identity": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.825.0.tgz", + "integrity": "sha512-cyL5xHqtvBUpflkmdQSkvjD/t+Dl/ZSXvPnc9KF79xDpuraZ5tFP1l0B6rIEu7dUzUh8XG+7m2CZ6TEs6QU33Q==", + "requires": { + "@aws-sdk/core": "3.825.0", + "@aws-sdk/nested-clients": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "@aws-sdk/middleware-host-header": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.821.0.tgz", + "integrity": "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==", "requires": { - "has-symbols": "^1.0.2" + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" } }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + "@aws-sdk/middleware-logger": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.821.0.tgz", + "integrity": "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==", + "requires": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "@aws-sdk/middleware-recursion-detection": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.821.0.tgz", + "integrity": "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==", + "requires": { + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } }, - "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==", + "@aws-sdk/middleware-user-agent": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.825.0.tgz", + "integrity": "sha512-3ZZOPU3GE5cqKl6VFDwiL8KIvlrrQJ4rgYkeiF+m5kA0eXV2xFOwoLgm3AmPB+6kfo9HQ0N74KKJV0teS5nO6Q==", "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "@aws-sdk/core": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@smithy/core": "^3.5.2", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" } }, - "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==" + "@aws-sdk/nested-clients": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.825.0.tgz", + "integrity": "sha512-OuV2pypFAv52Lty8eXWVWyyOywVmMAsgH6Gq3SA06pHEtcE+ghVIW9ByegecyfMRUpedAiovARKNy0pfGX05Pg==", + "requires": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.825.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.825.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.825.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.2", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.10", + "@smithy/middleware-retry": "^4.1.11", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.2", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.18", + "@smithy/util-defaults-mode-node": "^4.0.18", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + } }, - "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==", + "@aws-sdk/region-config-resolver": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.821.0.tgz", + "integrity": "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==", "requires": { - "has-tostringtag": "^1.0.0" + "@aws-sdk/types": "3.821.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" } }, - "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==", + "@aws-sdk/token-providers": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.825.0.tgz", + "integrity": "sha512-a3HbF6h1Gq2vA+mGlxFe3op65wNK6dBRmp3GFwsPVQ+OFTbZJi86FCljMfBrv+BGYUkp503/IPC49wuRHOdcZA==", "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" + "@aws-sdk/core": "3.825.0", + "@aws-sdk/nested-clients": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" } }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + "@aws-sdk/types": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.821.0.tgz", + "integrity": "sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA==", + "requires": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } }, - "jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" + "@aws-sdk/util-endpoints": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.821.0.tgz", + "integrity": "sha512-Uknt/zUZnLE76zaAAPEayOeF5/4IZ2puTFXvcSCWHsi9m3tqbb9UozlnlVqvCZLCRWfQryZQoG2W4XSS3qgk5A==", + "requires": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "@smithy/util-endpoints": "^3.0.6", + "tslib": "^2.6.2" + } }, - "node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + "@aws-sdk/util-locate-window": { + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz", + "integrity": "sha512-zVoRfpmBVPodYlnMjgVjfGoEZagyRF5IPn3Uo6ZvOZp24chnW/FRstH7ESDHDDRga4z3V+ElUQHKpFDXWyBW5A==", + "requires": { + "tslib": "^2.6.2" + } }, - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + "@aws-sdk/util-user-agent-browser": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.821.0.tgz", + "integrity": "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==", + "requires": { + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" + "@aws-sdk/util-user-agent-node": { + "version": "3.825.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.825.0.tgz", + "integrity": "sha512-RfB0w9YJSsFGsbrzOQ1VE2O4NwR6gxelUvmz8PzuerPCg4iD4JW7hSCmnoAEi51Xnq0bNeCsnhzXJzIlPe04jA==", + "requires": { + "@aws-sdk/middleware-user-agent": "3.825.0", + "@aws-sdk/types": "3.821.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } }, - "sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + "@aws-sdk/xml-builder": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz", + "integrity": "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==", + "requires": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } }, - "url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "@smithy/abort-controller": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", + "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" } }, - "util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "@smithy/config-resolver": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.4.tgz", + "integrity": "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==", "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" + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" } }, - "uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==" + "@smithy/core": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.5.3.tgz", + "integrity": "sha512-xa5byV9fEguZNofCclv6v9ra0FYh5FATQW/da7FQUVTic94DfrN/NvmKZjrMyzbpqfot9ZjBaO8U1UeTbmSLuA==", + "requires": { + "@smithy/middleware-serde": "^4.0.8", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + } }, - "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==", + "@smithy/credential-provider-imds": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.6.tgz", + "integrity": "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==", "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" + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "tslib": "^2.6.2" } }, - "xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "@smithy/fetch-http-handler": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.4.tgz", + "integrity": "sha512-AMtBR5pHppYMVD7z7G+OlHHAcgAN7v0kVKEpHuTO4Gb199Gowh0taYi9oDStFeUhetkeP55JLSVlTW1n9rFtUw==", "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" } }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + "@smithy/hash-node": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.4.tgz", + "integrity": "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==", + "requires": { + "@smithy/types": "^4.3.1", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/invalid-dependency": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.4.tgz", + "integrity": "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==", + "requires": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@smithy/is-array-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-content-length": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.4.tgz", + "integrity": "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==", + "requires": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-endpoint": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.11.tgz", + "integrity": "sha512-zDogwtRLzKl58lVS8wPcARevFZNBOOqnmzWWxVe9XiaXU2CADFjvJ9XfNibgkOWs08sxLuSr81NrpY4mgp9OwQ==", + "requires": { + "@smithy/core": "^3.5.3", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-retry": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.12.tgz", + "integrity": "sha512-wvIH70c4e91NtRxdaLZF+mbLZ/HcC6yg7ySKUiufL6ESp6zJUSnJucZ309AvG9nqCFHSRB5I6T3Ez1Q9wCh0Ww==", + "requires": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/service-error-classification": "^4.0.5", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "dependencies": { + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" + } + } + }, + "@smithy/middleware-serde": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz", + "integrity": "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==", + "requires": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@smithy/middleware-stack": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.4.tgz", + "integrity": "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==", + "requires": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@smithy/node-config-provider": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.3.tgz", + "integrity": "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==", + "requires": { + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@smithy/node-http-handler": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", + "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", + "requires": { + "@smithy/abort-controller": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@smithy/property-provider": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.4.tgz", + "integrity": "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==", + "requires": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@smithy/protocol-http": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.2.tgz", + "integrity": "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==", + "requires": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@smithy/querystring-builder": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.4.tgz", + "integrity": "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==", + "requires": { + "@smithy/types": "^4.3.1", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/querystring-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.4.tgz", + "integrity": "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==", + "requires": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@smithy/service-error-classification": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.5.tgz", + "integrity": "sha512-LvcfhrnCBvCmTee81pRlh1F39yTS/+kYleVeLCwNtkY8wtGg8V/ca9rbZZvYIl8OjlMtL6KIjaiL/lgVqHD2nA==", + "requires": { + "@smithy/types": "^4.3.1" + } + }, + "@smithy/shared-ini-file-loader": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.4.tgz", + "integrity": "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==", + "requires": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@smithy/signature-v4": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.2.tgz", + "integrity": "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==", + "requires": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/smithy-client": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.3.tgz", + "integrity": "sha512-xxzNYgA0HD6ETCe5QJubsxP0hQH3QK3kbpJz3QrosBCuIWyEXLR/CO5hFb2OeawEKUxMNhz3a1nuJNN2np2RMA==", + "requires": { + "@smithy/core": "^3.5.3", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", + "tslib": "^2.6.2" + } + }, + "@smithy/types": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", + "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/url-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.4.tgz", + "integrity": "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==", + "requires": { + "@smithy/querystring-parser": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@smithy/util-base64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", + "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", + "requires": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-body-length-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", + "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-body-length-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", + "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-buffer-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", + "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", + "requires": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-config-provider": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", + "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-defaults-mode-browser": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.19.tgz", + "integrity": "sha512-mvLMh87xSmQrV5XqnUYEPoiFFeEGYeAKIDDKdhE2ahqitm8OHM3aSvhqL6rrK6wm1brIk90JhxDf5lf2hbrLbQ==", + "requires": { + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-defaults-mode-node": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.19.tgz", + "integrity": "sha512-8tYnx+LUfj6m+zkUUIrIQJxPM1xVxfRBvoGHua7R/i6qAxOMjqR6CpEpDwKoIs1o0+hOjGvkKE23CafKL0vJ9w==", + "requires": { + "@smithy/config-resolver": "^4.1.4", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@smithy/util-endpoints": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.6.tgz", + "integrity": "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==", + "requires": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@smithy/util-hex-encoding": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-middleware": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.4.tgz", + "integrity": "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==", + "requires": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@smithy/util-retry": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.5.tgz", + "integrity": "sha512-V7MSjVDTlEt/plmOFBn1762Dyu5uqMrV2Pl2X0dYk4XvWfdWJNe9Bs5Bzb56wkCuiWjSfClVMGcsuKrGj7S/yg==", + "requires": { + "@smithy/service-error-classification": "^4.0.5", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@smithy/util-stream": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.2.tgz", + "integrity": "sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w==", + "requires": { + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + } + }, + "@smithy/util-uri-escape": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", + "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@smithy/util-utf8": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", + "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "requires": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + } + }, + "@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==" + }, + "bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + }, + "fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "requires": { + "strnum": "^1.0.5" + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, + "strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==" + }, + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" } } } diff --git a/private-ca/server/package.json b/private-ca/server/package.json index fa1d9a5..bd9f7a1 100644 --- a/private-ca/server/package.json +++ b/private-ca/server/package.json @@ -11,7 +11,7 @@ "author": "", "license": "ISC", "dependencies": { - "aws-sdk": "^2.1398.0", + "@aws-sdk/client-secrets-manager": "^3.825.0", "node-forge": "^1.3.1" } } diff --git a/private-ca/server/secret-manager-utils.js b/private-ca/server/secret-manager-utils.js index 1a622fd..2566c86 100644 --- a/private-ca/server/secret-manager-utils.js +++ b/private-ca/server/secret-manager-utils.js @@ -1,11 +1,13 @@ -import aws from "aws-sdk"; +import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager"; 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); + const client = new SecretsManagerClient({ region: secretRegion }); + const command = new GetSecretValueCommand({ SecretId: secretId }); + const response = await client.send(command); + const secret = JSON.parse(response.SecretString); return secret; -}; +} + /** export const updateSecret = async (secretRegion, secretId, key, value) => { From f2068ca8c4ced876dedb7c81e795635982b9a883 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 6 Jun 2025 15:31:29 +0530 Subject: [PATCH 053/146] prevent host cert generation in client environments --- private-ca/client/generate-certificate-curl.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index ac52adf..0e54c50 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -61,7 +61,7 @@ get_aws_credentials() { while getopts ":h" option; do case $option in h) - echo "Usage: ./generate-certificate.sh [ACTION] [PUBLIC KEY FILE] [LAMBDA URL]" + echo "Usage: ./generate-certificate.sh [ACTION] [CA URL] [ENVIRONMENT] [AWS PROFILE] [USER SSH DIR] [USER AWS DIR] [SYSTEM SSH DIR] [AWS STS REGION]" echo "Possible actions:" echo " generateHostSSHCert: Generates SSH Certificate for Host" echo " generateClientSSHCert: Generates SSH Certificate for Client" @@ -96,6 +96,12 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then CERT_PUBKEY=$(cat ${USER_SSH_DIR}/id_rsa.pub | base64 | tr -d \\n) elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then + # Host certificate generation is not allowed in client environment + if [[ $ENVIRONMENT = "client" ]]; then + echo -e "\nError: generateHostSSHCert is not allowed in client environment.\nHost certificate generation requires host/server environment.\n" + exit 1 + fi + 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") From 1bd830f430e2c62e5d5868f228183c51e013a1b1 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 6 Jun 2025 15:36:32 +0530 Subject: [PATCH 054/146] refactor: update generate-certificate-aws-cli.sh for improved AWS credential handling - Updated parameter order and added USER_AWS_DIR. - Added is_mfa_enabled and get_aws_credentials functions. - Ensured Python virtual environment is reused --- .../client/generate-certificate-aws-cli.sh | 120 ++++++++++++++---- 1 file changed, 94 insertions(+), 26 deletions(-) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index 0aa04e3..1972d12 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -1,28 +1,77 @@ #!/bin/bash CA_ACTION=${1:-"generateHostSSHCert"} -AWS_PROFILE=${2:-"default"} -USER_SSH_DIR=${3:-"/home/$USER/.ssh"} -SYSTEM_SSH_DIR=${4:-"/etc/ssh"} -LAMBDA_REGION=${5:-'us-west-2'} -CA_LAMBDA_FUNCTION_NAME=${6:-"privateCA"} -AWS_STS_REGION=${7:-"ap-southeast-1"} +ENVIRONMENT=${2:-"client"} +AWS_PROFILE=${3:-"default"} +USER_SSH_DIR=${4:-"/home/$USER/.ssh"} +USER_AWS_DIR=${5:-"/home/$USER/.aws"} +SYSTEM_SSH_DIR=${6:-"/etc/ssh"} +LAMBDA_REGION=${7:-'us-west-2'} +CA_LAMBDA_FUNCTION_NAME=${8:-"privateCA"} +AWS_STS_REGION=${9:-"ap-southeast-1"} + +PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) +[[ $? -ne 0 ]] && { echo "Python not installed."; exit 1; } + +is_mfa_enabled() { + grep -q 'get-credentials' ${USER_AWS_DIR}/credentials +} + +get_aws_credentials() { + local method=${1:-"host"} + + if [[ $method == "host" ]]; then + TOKEN=$(curl -s --max-time 30 -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 120") + + if [[ -z "$TOKEN" ]]; then + echo "Failed to fetch EC2 metadata token. Are you running this script on an EC2 instance?" + exit 1 + fi + + INSTANCE_ROLE_NAME=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/) + TEMP_CREDS=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$INSTANCE_ROLE_NAME) + PUBLIC_IP=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-ipv4) + + 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") + + elif [[ $method == "client" ]]; then + if is_mfa_enabled; then + CALLER_IDENTITY=$(aws sts get-caller-identity --profile $AWS_PROFILE) + [[ $? -ne 0 ]] && { echo "Your AWS credentials have either expired or are invalid. Please check your credentials and try again."; exit 1; } + + TEMP_CREDS=$(get-credentials $AWS_PROFILE) + 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 ".SessionToken") + else + TEMP_CREDS=$(aws sts get-session-token --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") + fi + else + echo "Invalid environment" + exit 1 + fi +} # Check for options while getopts ":h" option; do case $option in h) - echo "Usage: bash generate-certificate-aws-cli.sh [CA_ACTION] [AWS_PROFILE] [AWS_STS_REGION] [LAMBDA_REGION] [SYSTEM_SSH_DIR] [CA_LAMBDA_FUNCTION_NAME]" + echo "Usage: bash generate-certificate-aws-cli.sh [ACTION] [ENVIRONMENT] [AWS PROFILE] [USER SSH DIR] [USER AWS DIR] [SYSTEM SSH DIR] [AWS STS REGION]" echo "" echo "Parameters:" echo " CA_ACTION Action to perform (default: generateHostSSHCert)" - echo " AWS_PROFILE AWS profile to use (default: default)" - echo " USER_SSH_DIR User SSH directory path (default: /home/$USER/.ssh)" - echo " SYSTEM_SSH_DIR System SSH directory path (default: /etc/ssh)" - echo " LAMBDA_REGION Lambda function region (default: us-west-2)" - echo " CA_LAMBDA_FUNCTION_NAME Lambda function name (default: privateCA)" - echo " AWS_STS_REGION AWS STS region (default: ap-southeast-1)" - echo "" + echo " ENVIRONMENT Environment to use (default: client)" + echo " AWS PROFILE AWS profile to use (default: default)" + echo " USER SSH DIR Path to user's SSH directory (default: /home/$USER/.ssh)" + echo " USER AWS DIR Path to user's AWS directory (default: /home/$USER/.aws)" + echo " SYSTEM SSH DIR Path to system's SSH directory (default: /etc/ssh)" + echo " AWS STS REGION AWS region for STS operations (default: ap-southeast-1)" + echo "" echo "CA_ACTION:" echo " generateHostSSHCert Generates SSH Certificate for Host" echo " generateClientSSHCert Generates SSH Certificate for Client" @@ -36,6 +85,9 @@ done # Check for CA Action if [[ $CA_ACTION = "generateClientSSHCert" ]]; then + + [[ -d "${USER_SSH_DIR}" ]] || { echo "User SSH directory does not exist. Please provide the correct user SSH directory."; exit 1; } + 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") @@ -55,6 +107,12 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then CERT_PUBKEY=$(cat ${USER_SSH_DIR}/id_rsa.pub | base64 | tr -d \\n) elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then + # Host certificate generation is not allowed in client environment + if [[ $ENVIRONMENT = "client" ]]; then + echo -e "\nError: generateHostSSHCert is not allowed in client environment.\nHost certificate generation requires host/server environment.\n" + exit 1 + fi + 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") @@ -81,23 +139,23 @@ else fi # Temporary Credentials -TEMP_CREDS=$(aws sts get-session-token --profile $AWS_PROFILE) +get_aws_credentials $ENVIRONMENT -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") -PUBLIC_IP="" +if [ ! -d "env" ]; then + $PYTHON_EXEC -m venv env +fi +source env/bin/activate +pip install -q --upgrade boto3 -PYTHON_EXEC=$(which python || which python3) +# Update PYTHON_EXEC to use the Python executable from the activated virtual environment +# This ensures we use the venv's Python with the installed dependencies (boto3) +PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) # 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") -CERT_PUBKEY=$(cat ${SYSTEM_SSH_DIR}/ssh_host_rsa_key.pub | base64 | tr -d \\n) + INNER_JSON=$(jq -n \ --arg amzDate "$date" \ @@ -123,13 +181,21 @@ INNER_JSON=$(jq -n \ json_body=$(jq -n --arg body "$INNER_JSON" '{body: $body}') echo "$json_body" > event.json +# Use --profile only if AWS_PROFILE is set and ~/.aws/credentials exists +# On EC2 instances with IAM roles, credentials are fetched from instance metadata, +# and using --profile will cause an error if ~/.aws/credentials doesn't exist. +AWS_PROFILE_ARG="" +if [[ -n "$AWS_PROFILE" && -f ~/.aws/credentials ]]; then + AWS_PROFILE_ARG="--profile $AWS_PROFILE" +fi + 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 \ - --profile $AWS_PROFILE + $AWS_PROFILE_ARG response_body=$(cat response.json | jq -r ".body") @@ -141,6 +207,8 @@ 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" + [[ -f "${USER_SSH_DIR}/known_hosts" ]] || touch "${USER_SSH_DIR}/known_hosts" + 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 @@ -166,4 +234,4 @@ fi # Clean up deactivate -sudo rm -r env *.json +rm -r *.json From 698d95e2cd7de77e20e53e3941a092ddf281c6f2 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Mon, 9 Jun 2025 15:19:08 +0530 Subject: [PATCH 055/146] refactor: improve AWS credential handling by removing redundant declarations --- .../client/generate-certificate-curl.sh | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 0e54c50..f9bf15a 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -18,43 +18,36 @@ is_mfa_enabled() { get_aws_credentials() { local method=${1:-"host"} - + local TEMP_CREDS + if [[ $method == "host" ]]; then TOKEN=$(curl -s --max-time 30 -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 120") - + if [[ -z "$TOKEN" ]]; then echo "Failed to fetch EC2 metadata token. Are you running this script on an EC2 instance?" exit 1 fi - + INSTANCE_ROLE_NAME=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/) TEMP_CREDS=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$INSTANCE_ROLE_NAME) PUBLIC_IP=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-ipv4) - 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") - elif [[ $method == "client" ]]; then if is_mfa_enabled; then CALLER_IDENTITY=$(aws sts get-caller-identity --profile $AWS_PROFILE) [[ $? -ne 0 ]] && { echo "Your AWS credentials have either expired or are invalid. Please check your credentials and try again."; exit 1; } - - TEMP_CREDS=$(get-credentials $AWS_PROFILE) - 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 ".SessionToken") + TEMP_CREDS=$(get-credentials $AWS_PROFILE) else - TEMP_CREDS=$(aws sts get-session-token --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") + TEMP_CREDS=$(aws sts get-session-token --profile $AWS_PROFILE | jq -r ".Credentials") fi else - echo "Invalid environment" - exit 1 + echo "Invalid environment"; exit 1; fi + + 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 // .SessionToken // .Sessiontoken") } # Check for options From 41037d76210c9398c6bde3c60c7b047545f6fa14 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Mon, 9 Jun 2025 15:21:01 +0530 Subject: [PATCH 056/146] update README.md: add instructions for generating certificates via AWS CLI script --- private-ca/client/README.md | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/private-ca/client/README.md b/private-ca/client/README.md index e4cbb66..eeac84a 100644 --- a/private-ca/client/README.md +++ b/private-ca/client/README.md @@ -35,6 +35,47 @@ bash generate-certificate-curl.sh generateHostSSHCert host 1. Sudo privilege is required for generating host certificates as they need to write to system directories like `/etc/ssh`. 2. The `ENVIRONMENT` (host or client) parameter only affects how AWS credentials are retrieved, not what type of certificates you can generate. See [Script Parameters](#script-parameters) for more details. +### Running via AWS CLI (Lambda) + +The `generate-certificate-aws-cli.sh` script provides an alternative approach to generate certificates. This method uses AWS CLI to invoke a Lambda function rather than making direct HTTP requests. + +#### Usage: + +```bash +bash generate-certificate-aws-cli.sh +``` + +#### AWS CLI Script Parameters: + +1. **CA_ACTION** (required): The action to perform + + - `generateClientSSHCert`: Generates an SSH certificate for clients + - `generateHostSSHCert`: Generates an SSH certificate for hosts + +2. **ENVIRONMENT** (optional): Machine environment type - "client" or "host" (defaults to "client") + + - **"host"**: For EC2 instances - uses EC2 instance metadata for AWS credentials + - **"client"**: For local user machines - uses AWS CLI/STS for credentials + - _Note: This parameter only affects how AWS credentials are retrieved, not what type of certificates you can generate_ + +3. **AWS_PROFILE** (optional): AWS profile name (defaults to "default") + +4. **USER_SSH_DIR** (optional): Path to user's SSH directory (defaults to "/home/$USER/.ssh") + +5. **SYSTEM_SSH_DIR** (optional): Path to system SSH directory (defaults to "/etc/ssh") + +6. **LAMBDA_REGION** (optional): AWS region where the Lambda function is deployed (defaults to "us-west-2") + +7. **CA_LAMBDA_FUNCTION_NAME** (optional): Name of the Lambda function (defaults to "privateCA") + +8. **AWS_STS_REGION** (optional): AWS region for STS operations (defaults to "ap-southeast-1") + +**Requirements for AWS CLI method:** + +- AWS CLI configured with appropriate credentials +- Python 3 +- Access to the Lambda function in the specified region + ### Running via Docker 1. Build the Docker image: From 4e15f38c5f27e6923615c15f52f55ecac18bbbf8 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Mon, 9 Jun 2025 15:25:47 +0530 Subject: [PATCH 057/146] update README.md --- private-ca/client/README.md | 62 ++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/private-ca/client/README.md b/private-ca/client/README.md index eeac84a..fb207b1 100644 --- a/private-ca/client/README.md +++ b/private-ca/client/README.md @@ -14,6 +14,12 @@ This project provides tools to generate SSH certificates using a Private Certifi - Bash - Dependencies: `curl`, `jq`, `ssh-keygen`, `base64` +### Running via AWS CLI (Lambda) + +- AWS CLI +- Python 3 +- Access to the Lambda function in the specified region + ## Usage ### Running directly @@ -45,37 +51,6 @@ The `generate-certificate-aws-cli.sh` script provides an alternative approach to bash generate-certificate-aws-cli.sh ``` -#### AWS CLI Script Parameters: - -1. **CA_ACTION** (required): The action to perform - - - `generateClientSSHCert`: Generates an SSH certificate for clients - - `generateHostSSHCert`: Generates an SSH certificate for hosts - -2. **ENVIRONMENT** (optional): Machine environment type - "client" or "host" (defaults to "client") - - - **"host"**: For EC2 instances - uses EC2 instance metadata for AWS credentials - - **"client"**: For local user machines - uses AWS CLI/STS for credentials - - _Note: This parameter only affects how AWS credentials are retrieved, not what type of certificates you can generate_ - -3. **AWS_PROFILE** (optional): AWS profile name (defaults to "default") - -4. **USER_SSH_DIR** (optional): Path to user's SSH directory (defaults to "/home/$USER/.ssh") - -5. **SYSTEM_SSH_DIR** (optional): Path to system SSH directory (defaults to "/etc/ssh") - -6. **LAMBDA_REGION** (optional): AWS region where the Lambda function is deployed (defaults to "us-west-2") - -7. **CA_LAMBDA_FUNCTION_NAME** (optional): Name of the Lambda function (defaults to "privateCA") - -8. **AWS_STS_REGION** (optional): AWS region for STS operations (defaults to "ap-southeast-1") - -**Requirements for AWS CLI method:** - -- AWS CLI configured with appropriate credentials -- Python 3 -- Access to the Lambda function in the specified region - ### Running via Docker 1. Build the Docker image: @@ -145,6 +120,31 @@ The `generate-certificate-curl.sh` script accepts the following parameters: 8. **AWS_STS_REGION** (optional): AWS region for STS (defaults to "ap-southeast-1") +The `generate-certificate-aws-cli.sh` script accepts the following parameters: + +1. **CA_ACTION** (required): The action to perform + + - `generateClientSSHCert`: Generates an SSH certificate for clients + - `generateHostSSHCert`: Generates an SSH certificate for hosts + +2. **ENVIRONMENT** (optional): Machine environment type - "client" or "host" (defaults to "client") + + - **"host"**: For EC2 instances - uses EC2 instance metadata for AWS credentials + - **"client"**: For local user machines - uses AWS CLI/STS for credentials + - _Note: This parameter only affects how AWS credentials are retrieved, not what type of certificates you can generate_ + +3. **AWS_PROFILE** (optional): AWS profile name (defaults to "default") + +4. **USER_SSH_DIR** (optional): Path to user's SSH directory (defaults to "/home/$USER/.ssh") + +5. **SYSTEM_SSH_DIR** (optional): Path to system SSH directory (defaults to "/etc/ssh") + +6. **LAMBDA_REGION** (optional): AWS region where the Lambda function is deployed (defaults to "us-west-2") + +7. **CA_LAMBDA_FUNCTION_NAME** (optional): Name of the Lambda function (defaults to "privateCA") + +8. **AWS_STS_REGION** (optional): AWS region for STS operations (defaults to "ap-southeast-1") + ## Important Notes - **Environment vs Certificate Type**: The `ENVIRONMENT` parameter determines how AWS credentials are retrieved, not what certificates you can generate From 3ce024fb824d53ee4387ea3bfdd9b84f74871f82 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Mon, 9 Jun 2025 15:35:32 +0530 Subject: [PATCH 058/146] update README with ENVIRONMENT param usage and client environment limitations --- private-ca/client/README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/private-ca/client/README.md b/private-ca/client/README.md index fb207b1..a226c50 100644 --- a/private-ca/client/README.md +++ b/private-ca/client/README.md @@ -39,7 +39,7 @@ bash generate-certificate-curl.sh generateHostSSHCert host **Note:** 1. Sudo privilege is required for generating host certificates as they need to write to system directories like `/etc/ssh`. -2. The `ENVIRONMENT` (host or client) parameter only affects how AWS credentials are retrieved, not what type of certificates you can generate. See [Script Parameters](#script-parameters) for more details. +2. The `ENVIRONMENT` (host or client) parameter affects how AWS credentials are retrieved. See [Script Parameters](#script-parameters) for more details. ### Running via AWS CLI (Lambda) @@ -108,7 +108,6 @@ The `generate-certificate-curl.sh` script accepts the following parameters: - **"host"**: For EC2 instances - uses EC2 instance metadata for AWS credentials - **"client"**: For local user machines - uses AWS CLI/STS for credentials - - _Note: This parameter only affects how AWS credentials are retrieved, not what type of certificates you can generate_ 4. **AWS_PROFILE** (optional): AWS profile name (defaults to "default") @@ -131,7 +130,6 @@ The `generate-certificate-aws-cli.sh` script accepts the following parameters: - **"host"**: For EC2 instances - uses EC2 instance metadata for AWS credentials - **"client"**: For local user machines - uses AWS CLI/STS for credentials - - _Note: This parameter only affects how AWS credentials are retrieved, not what type of certificates you can generate_ 3. **AWS_PROFILE** (optional): AWS profile name (defaults to "default") @@ -147,13 +145,16 @@ The `generate-certificate-aws-cli.sh` script accepts the following parameters: ## Important Notes -- **Environment vs Certificate Type**: The `ENVIRONMENT` parameter determines how AWS credentials are retrieved, not what certificates you can generate - - You can generate both client and host certificates from either environment type - - EC2 instances should use `ENVIRONMENT=host` for credential retrieval via instance metadata - - Local user machines should use `ENVIRONMENT=client` for credential retrieval via AWS CLI - **Certificate Type**: Determined by the `CA_ACTION` parameter (`generateClientSSHCert` or `generateHostSSHCert`) - **Permissions**: Host certificates require sudo privileges for system directory access +## Client Environment Limitations + +**Important**: Client environments can only generate client certificates because they don't have a public IP address. + +- **Host Certificate Requirements**: Host certificates require the public IP address as a hostname when issuing the certificate. Due to the absence of a public IP address, client environments cannot generate host certificates +- **Recommendation**: Use client environments exclusively for generating client certificates, and use host environments (such as EC2 instances with public IPs) for generating host certificates + ## Directory Structure - `generate-certificate-curl.sh`: Main script for certificate generation using curl From 78f449f879ab48573f78f5ae1539fccbf2f4d1e0 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 20 Jun 2025 12:28:05 +0530 Subject: [PATCH 059/146] feat: add function to get public IP of host using Describe Instances from Lambda + error handling in client script --- .../client/generate-certificate-curl.sh | 51 +- private-ca/server/generate-host-ssh-cert.js | 14 +- private-ca/server/get-public-ip-address.js | 26 + private-ca/server/index_lambda.js | 2 +- private-ca/server/package-lock.json | 811 +++++++++++++++++- private-ca/server/package.json | 1 + 6 files changed, 851 insertions(+), 54 deletions(-) create mode 100644 private-ca/server/get-public-ip-address.js diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index f9bf15a..ebd07a0 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -1,4 +1,6 @@ #!/bin/bash +set -eo pipefail +trap 'echo "Error occurred on line $LINENO. Exiting."; exit 1;' ERR CA_ACTION=${1:-$CA_ACTION} CA_URL=${2:-$CA_URL} @@ -8,6 +10,7 @@ USER_SSH_DIR=${5:-"/home/$USER/.ssh"} USER_AWS_DIR=${6:-"/home/$USER/.aws"} SYSTEM_SSH_DIR=${7:-"/etc/ssh"} AWS_STS_REGION=${8:-"ap-southeast-1"} +AWS_EC2_REGION=${9:-"us-west-2"} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) [[ $? -ne 0 ]] && { echo "Python not installed."; exit 1; } @@ -30,7 +33,6 @@ get_aws_credentials() { INSTANCE_ROLE_NAME=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/) TEMP_CREDS=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$INSTANCE_ROLE_NAME) - PUBLIC_IP=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-ipv4) elif [[ $method == "client" ]]; then if is_mfa_enabled; then @@ -133,16 +135,31 @@ pip install -q --upgrade boto3 PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) # Auth Headers -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") +output=$($PYTHON_EXEC aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_STS_REGION) || { + echo "Failed to generate auth header. Check aws-auth-header.py script."; + exit 1; +} +auth_header=$(echo "$output" | jq -er ".Authorization") || { + echo "Failed to parse Authorization from auth-header output."; + exit 1; +} +date=$(echo "$output" | jq -er ".Date") || { + echo "Failed to parse Date from auth-header output."; + exit 1; +} -EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\",\"publicIp\":\"${PUBLIC_IP}\"}") +EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\",\"awsEC2Region\":\"${AWS_EC2_REGION}\"}") if [[ $CA_ACTION = "generateClientSSHCert" ]]; then - LAMBDA_RESPONSE=$(curl "${CA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON") - ENCODED_CERTIFICATE=$(echo $LAMBDA_RESPONSE | jq -r ".certificate") + LAMBDA_RESPONSE=$(curl -sf "${CA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON") || { + echo "Failed to contact Lambda CA URL. Ensure the URL is correct and the server is running."; + exit 1; + } + ENCODED_CERTIFICATE=$(echo "$LAMBDA_RESPONSE" | jq -er ".certificate") || { + echo "Certificate not found in Lambda response. Aborting."; + exit 1; + } CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) HOST_CA_PUBKEY=$(echo $LAMBDA_RESPONSE | jq -r ".\"host_ca.pub\"" | base64 -d) @@ -157,13 +174,25 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then # sudo access is required to generate host certificate elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then - LAMBDA_RESPONSE=$(curl "${CA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON") - ENCODED_CERTIFICATE=$(echo $LAMBDA_RESPONSE | jq -r ".certificate") + LAMBDA_RESPONSE=$(curl -sf "${CA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON") || { + echo "Failed to contact Lambda CA URL. Ensure the URL is correct and the server is running."; + exit 1; + } + echo "res: $LAMBDA_RESPONSE" + ENCODED_CERTIFICATE=$(echo "$LAMBDA_RESPONSE" | jq -er ".certificate") || { + echo "Certificate not found in Lambda response. Aborting."; + exit 1; + } 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" + if [[ -n "$CERTIFICATE" ]]; then + echo "$CERTIFICATE" > "${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" + echo "Certificate written to ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" + else + echo "Empty certificate received. Not writing to disk." + exit 1 + fi test -f ${SYSTEM_SSH_DIR}/user_ca.pub || echo $USER_CA_PUBKEY > ${SYSTEM_SSH_DIR}/user_ca.pub diff --git a/private-ca/server/generate-host-ssh-cert.js b/private-ca/server/generate-host-ssh-cert.js index cf9a9df..bfcc917 100644 --- a/private-ca/server/generate-host-ssh-cert.js +++ b/private-ca/server/generate-host-ssh-cert.js @@ -1,14 +1,20 @@ import fs from 'fs'; import child_process from 'child_process'; import util from 'util'; +import { getPublicIpAddress } from './get-public-ip-address.js'; const exec = util.promisify(child_process.exec); -export const signHostSSHCertificate = async (callerIdentity, secret, certPubkey, publicIp) => { +export const signHostSSHCertificate = async (callerIdentity, secret, certPubkey, awsEC2Region) => { const arn = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn; - const roleName = arn.match(/\/([^/]+)$/)?.[1]; - + const instanceId = arn.match(/\/([^/]+)$/)?.[1]; + + const publicIp = await getPublicIpAddress({ + awsEC2Region: awsEC2Region, + instanceId: instanceId + }); + const caKeyPath = "/tmp/host_ca"; const publicKeyName = "ssh_host_rsa_key"; const publicKeyPath = "/tmp/" + publicKeyName + ".pub"; @@ -22,7 +28,7 @@ export const signHostSSHCertificate = async (callerIdentity, secret, certPubkey, console.log('stdout:', stdout); console.log('stderr:', stderr); - ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I host_${roleName} -h -n ${publicIp} -V +1d ${publicKeyPath}`)); + ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I host_${instanceId} -h -n ${publicIp} -V +1d ${publicKeyPath}`)); console.log('stdout:', stdout); console.log('stderr:', stderr); diff --git a/private-ca/server/get-public-ip-address.js b/private-ca/server/get-public-ip-address.js new file mode 100644 index 0000000..1c8afa5 --- /dev/null +++ b/private-ca/server/get-public-ip-address.js @@ -0,0 +1,26 @@ +import { EC2Client, DescribeInstancesCommand } from "@aws-sdk/client-ec2"; + +export const getPublicIpAddress = async (event) => { + const region = event.awsEC2Region; + const instanceId = event.instanceId; + + const client = new EC2Client({ region }); + + const command = new DescribeInstancesCommand({ + InstanceIds: [instanceId], + }); + + try { + const response = await client.send(command); + + const instance = response.Reservations?.[0]?.Instances?.[0]; + + if (!instance) { + throw new Error(`Instance ${instanceId} not found`); + } + + return instance.PublicIpAddress; + } catch (error) { + throw error; + } +}; \ No newline at end of file diff --git a/private-ca/server/index_lambda.js b/private-ca/server/index_lambda.js index 3d5179c..1aca1cc 100644 --- a/private-ca/server/index_lambda.js +++ b/private-ca/server/index_lambda.js @@ -17,7 +17,7 @@ export const handler = async (event) => { // action switch(event.action) { case "generateHostSSHCert": - const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.certPubkey, event.publicIp); + const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.certPubkey, event.awsEC2Region); return { statusCode: 200, body: "{\"certificate\" : \""+Buffer.from(hostSSHCert).toString('base64')+"\", \"user_ca.pub\": \""+secret["user_ca.pub"]+"\"}" diff --git a/private-ca/server/package-lock.json b/private-ca/server/package-lock.json index 4fb39af..120901e 100644 --- a/private-ca/server/package-lock.json +++ b/private-ca/server/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "ISC", "dependencies": { + "@aws-sdk/client-ec2": "^3.827.0", "@aws-sdk/client-secrets-manager": "^3.825.0", "node-forge": "^1.3.1" } @@ -128,6 +129,367 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/client-ec2": { + "version": "3.827.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-ec2/-/client-ec2-3.827.0.tgz", + "integrity": "sha512-vOALW4vUcsk9+dlNIgyyN5msKpy7TN+TxkFeHwVQOXPzkL5/0V+x1UWxxmUKnhOXxfG2JK3KD9+A9S9IkMHCKQ==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/credential-provider-node": "3.826.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-sdk-ec2": "3.826.0", + "@aws-sdk/middleware-user-agent": "3.826.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.826.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.3", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-retry": "^4.1.12", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.19", + "@smithy/util-defaults-mode-node": "^4.0.19", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "@smithy/util-waiter": "^4.0.5", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sso": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.826.0.tgz", + "integrity": "sha512-/FEKnUC3xPkLL4RuRydwzx+y4b55HIX6qLPbGnyIs+sNmCUyc/62ijtV1Ml+b++YzEF6jWNBsJOxeyZdgrJ3Ig==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.826.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.826.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.3", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-retry": "^4.1.12", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.19", + "@smithy/util-defaults-mode-node": "^4.0.19", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/core": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.826.0.tgz", + "integrity": "sha512-BGbQYzWj3ps+dblq33FY5tz/SsgJCcXX0zjQlSC07tYvU1jHTUvsefphyig+fY38xZ4wdKjbTop+KUmXUYrOXw==", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/core": "^3.5.3", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-utf8": "^4.0.0", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.826.0.tgz", + "integrity": "sha512-DK3pQY8+iKK3MGDdC3uOZQ2psU01obaKlTYhEwNu4VWzgwQL4Vi3sWj4xSWGEK41vqZxiRLq6fOq7ysRI+qEZA==", + "dependencies": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.826.0.tgz", + "integrity": "sha512-N+IVZBh+yx/9GbMZTKO/gErBi/FYZQtcFRItoLbY+6WU+0cSWyZYfkoeOxHmQV3iX9k65oljERIWUmL9x6OSQg==", + "dependencies": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.826.0.tgz", + "integrity": "sha512-g7n+qSklq/Lzjxe2Ke5QFNCgYn26a3ydZnbFIk8QqYin4pzG+qiunaqJjpV3c/EeHMlfK8bBc7MXAylKzGRccQ==", + "dependencies": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/credential-provider-env": "3.826.0", + "@aws-sdk/credential-provider-http": "3.826.0", + "@aws-sdk/credential-provider-process": "3.826.0", + "@aws-sdk/credential-provider-sso": "3.826.0", + "@aws-sdk/credential-provider-web-identity": "3.826.0", + "@aws-sdk/nested-clients": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.826.0.tgz", + "integrity": "sha512-UfIJXxHjmSxH6bea00HBPLkjNI2D04enQA/xNLZvB+4xtzt1/gYdCis1P4/73f5aGVVVB4/zQMobBbnjkrmbQw==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.826.0", + "@aws-sdk/credential-provider-http": "3.826.0", + "@aws-sdk/credential-provider-ini": "3.826.0", + "@aws-sdk/credential-provider-process": "3.826.0", + "@aws-sdk/credential-provider-sso": "3.826.0", + "@aws-sdk/credential-provider-web-identity": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.826.0.tgz", + "integrity": "sha512-kURrc4amu3NLtw1yZw7EoLNEVhmOMRUTs+chaNcmS+ERm3yK0nKjaJzmKahmwlTQTSl3wJ8jjK7x962VPo+zWw==", + "dependencies": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.826.0.tgz", + "integrity": "sha512-F19J3zcfoom6OnQ0MyAtvduVKQXPgkz9i5ExSO01J2CzjbyMhCDA99qAjHYe+LwhW+W7P/jzBPd0+uOQ2Nhh9Q==", + "dependencies": { + "@aws-sdk/client-sso": "3.826.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/token-providers": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.826.0.tgz", + "integrity": "sha512-o27GZ6Hy7qhuvMFVUL2eFEpBzf33Jaa/x3u3SHwU0nL7ko7jmbpeF0x4+wmagpI9X2IvVlUxIs0VaQ3YayPLEA==", + "dependencies": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/nested-clients": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.826.0.tgz", + "integrity": "sha512-j404+EcfBbtTlAhyObjXbdKwwDXO1pCxHvR5Fw8FXNvp/H330j6YnXgs3SJ6d3bZUwUJ/ztPx2S5AlBbLVLDFw==", + "dependencies": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@smithy/core": "^3.5.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/nested-clients": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.826.0.tgz", + "integrity": "sha512-p7olPq0uTtHqGuXI1GSc/gzKDvV55PMbLtnmupEDfnY9SoRu+QatbWQ6da9sI1lhOcNmRMgiNQBXFzaUFrG+SQ==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.826.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.826.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.3", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-retry": "^4.1.12", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.19", + "@smithy/util-defaults-mode-node": "^4.0.19", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/token-providers": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.826.0.tgz", + "integrity": "sha512-iCOcVAqGPSHtQL8ZBXifZMEcHyUl9wJ8HvLZ5l1ohA/3ZNP+dqEPGi7jfhR5jZKs+xyp2jxByFqfil9PjI9c5A==", + "dependencies": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/nested-clients": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.826.0.tgz", + "integrity": "sha512-wHw6bZQWIMcFF/8r03aY9Itp6JLBYY4absGGhCDK1dc3tPEfi8NVSdb05a/Oz+g4TVaDdxLo0OQ/OKMS1DFRHQ==", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, "node_modules/@aws-sdk/client-secrets-manager": { "version": "3.825.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.825.0.tgz", @@ -179,18 +541,6 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-secrets-manager/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@aws-sdk/client-sso": { "version": "3.825.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.825.0.tgz", @@ -435,6 +785,24 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/middleware-sdk-ec2": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-ec2/-/middleware-sdk-ec2-3.826.0.tgz", + "integrity": "sha512-qonwFJddYtVTXEj+GGjqHWqlSYDmKI4ZIf7iozNgucBzP5+zdFuyjvdQIAFLAv/joQphGv4P799PK2Elb5ZzpA==", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-format-url": "3.821.0", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aws-sdk/middleware-user-agent": { "version": "3.825.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.825.0.tgz", @@ -559,6 +927,20 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.821.0.tgz", + "integrity": "sha512-h+xqmPToxDrZ0a7rxE1a8Oh4zpWfZe9oiQUphGtfiGFA6j75UiURH5J3MmGHa/G4t15I3iLLbYtUXxvb1i7evg==", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aws-sdk/util-locate-window": { "version": "3.804.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz", @@ -779,18 +1161,6 @@ "node": ">=18.0.0" } }, - "node_modules/@smithy/middleware-retry/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@smithy/middleware-serde": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz", @@ -1156,6 +1526,19 @@ "node": ">=18.0.0" } }, + "node_modules/@smithy/util-waiter": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.5.tgz", + "integrity": "sha512-4QvC49HTteI1gfemu0I1syWovJgPvGn7CVUoN9ZFkdvr/cCFkrEL7qNCdx/2eICqDWEGnnr68oMdSIPCLAriSQ==", + "dependencies": { + "@smithy/abort-controller": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@types/uuid": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", @@ -1210,6 +1593,18 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } } }, "dependencies": { @@ -1311,6 +1706,319 @@ } } }, + "@aws-sdk/client-ec2": { + "version": "3.827.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-ec2/-/client-ec2-3.827.0.tgz", + "integrity": "sha512-vOALW4vUcsk9+dlNIgyyN5msKpy7TN+TxkFeHwVQOXPzkL5/0V+x1UWxxmUKnhOXxfG2JK3KD9+A9S9IkMHCKQ==", + "requires": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/credential-provider-node": "3.826.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-sdk-ec2": "3.826.0", + "@aws-sdk/middleware-user-agent": "3.826.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.826.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.3", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-retry": "^4.1.12", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.19", + "@smithy/util-defaults-mode-node": "^4.0.19", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "@smithy/util-waiter": "^4.0.5", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "dependencies": { + "@aws-sdk/client-sso": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.826.0.tgz", + "integrity": "sha512-/FEKnUC3xPkLL4RuRydwzx+y4b55HIX6qLPbGnyIs+sNmCUyc/62ijtV1Ml+b++YzEF6jWNBsJOxeyZdgrJ3Ig==", + "requires": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.826.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.826.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.3", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-retry": "^4.1.12", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.19", + "@smithy/util-defaults-mode-node": "^4.0.19", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/core": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.826.0.tgz", + "integrity": "sha512-BGbQYzWj3ps+dblq33FY5tz/SsgJCcXX0zjQlSC07tYvU1jHTUvsefphyig+fY38xZ4wdKjbTop+KUmXUYrOXw==", + "requires": { + "@aws-sdk/types": "3.821.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/core": "^3.5.3", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-utf8": "^4.0.0", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-env": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.826.0.tgz", + "integrity": "sha512-DK3pQY8+iKK3MGDdC3uOZQ2psU01obaKlTYhEwNu4VWzgwQL4Vi3sWj4xSWGEK41vqZxiRLq6fOq7ysRI+qEZA==", + "requires": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-http": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.826.0.tgz", + "integrity": "sha512-N+IVZBh+yx/9GbMZTKO/gErBi/FYZQtcFRItoLbY+6WU+0cSWyZYfkoeOxHmQV3iX9k65oljERIWUmL9x6OSQg==", + "requires": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-ini": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.826.0.tgz", + "integrity": "sha512-g7n+qSklq/Lzjxe2Ke5QFNCgYn26a3ydZnbFIk8QqYin4pzG+qiunaqJjpV3c/EeHMlfK8bBc7MXAylKzGRccQ==", + "requires": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/credential-provider-env": "3.826.0", + "@aws-sdk/credential-provider-http": "3.826.0", + "@aws-sdk/credential-provider-process": "3.826.0", + "@aws-sdk/credential-provider-sso": "3.826.0", + "@aws-sdk/credential-provider-web-identity": "3.826.0", + "@aws-sdk/nested-clients": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-node": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.826.0.tgz", + "integrity": "sha512-UfIJXxHjmSxH6bea00HBPLkjNI2D04enQA/xNLZvB+4xtzt1/gYdCis1P4/73f5aGVVVB4/zQMobBbnjkrmbQw==", + "requires": { + "@aws-sdk/credential-provider-env": "3.826.0", + "@aws-sdk/credential-provider-http": "3.826.0", + "@aws-sdk/credential-provider-ini": "3.826.0", + "@aws-sdk/credential-provider-process": "3.826.0", + "@aws-sdk/credential-provider-sso": "3.826.0", + "@aws-sdk/credential-provider-web-identity": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-process": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.826.0.tgz", + "integrity": "sha512-kURrc4amu3NLtw1yZw7EoLNEVhmOMRUTs+chaNcmS+ERm3yK0nKjaJzmKahmwlTQTSl3wJ8jjK7x962VPo+zWw==", + "requires": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-sso": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.826.0.tgz", + "integrity": "sha512-F19J3zcfoom6OnQ0MyAtvduVKQXPgkz9i5ExSO01J2CzjbyMhCDA99qAjHYe+LwhW+W7P/jzBPd0+uOQ2Nhh9Q==", + "requires": { + "@aws-sdk/client-sso": "3.826.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/token-providers": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/credential-provider-web-identity": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.826.0.tgz", + "integrity": "sha512-o27GZ6Hy7qhuvMFVUL2eFEpBzf33Jaa/x3u3SHwU0nL7ko7jmbpeF0x4+wmagpI9X2IvVlUxIs0VaQ3YayPLEA==", + "requires": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/nested-clients": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/middleware-user-agent": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.826.0.tgz", + "integrity": "sha512-j404+EcfBbtTlAhyObjXbdKwwDXO1pCxHvR5Fw8FXNvp/H330j6YnXgs3SJ6d3bZUwUJ/ztPx2S5AlBbLVLDFw==", + "requires": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@smithy/core": "^3.5.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/nested-clients": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.826.0.tgz", + "integrity": "sha512-p7olPq0uTtHqGuXI1GSc/gzKDvV55PMbLtnmupEDfnY9SoRu+QatbWQ6da9sI1lhOcNmRMgiNQBXFzaUFrG+SQ==", + "requires": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.826.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.826.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.3", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-retry": "^4.1.12", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.19", + "@smithy/util-defaults-mode-node": "^4.0.19", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/token-providers": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.826.0.tgz", + "integrity": "sha512-iCOcVAqGPSHtQL8ZBXifZMEcHyUl9wJ8HvLZ5l1ohA/3ZNP+dqEPGi7jfhR5jZKs+xyp2jxByFqfil9PjI9c5A==", + "requires": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/nested-clients": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, + "@aws-sdk/util-user-agent-node": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.826.0.tgz", + "integrity": "sha512-wHw6bZQWIMcFF/8r03aY9Itp6JLBYY4absGGhCDK1dc3tPEfi8NVSdb05a/Oz+g4TVaDdxLo0OQ/OKMS1DFRHQ==", + "requires": { + "@aws-sdk/middleware-user-agent": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + } + } + }, "@aws-sdk/client-secrets-manager": { "version": "3.825.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.825.0.tgz", @@ -1357,13 +2065,6 @@ "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" - }, - "dependencies": { - "uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" - } } }, "@aws-sdk/client-sso": { @@ -1574,6 +2275,21 @@ "tslib": "^2.6.2" } }, + "@aws-sdk/middleware-sdk-ec2": { + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-ec2/-/middleware-sdk-ec2-3.826.0.tgz", + "integrity": "sha512-qonwFJddYtVTXEj+GGjqHWqlSYDmKI4ZIf7iozNgucBzP5+zdFuyjvdQIAFLAv/joQphGv4P799PK2Elb5ZzpA==", + "requires": { + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-format-url": "3.821.0", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, "@aws-sdk/middleware-user-agent": { "version": "3.825.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.825.0.tgz", @@ -1680,6 +2396,17 @@ "tslib": "^2.6.2" } }, + "@aws-sdk/util-format-url": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.821.0.tgz", + "integrity": "sha512-h+xqmPToxDrZ0a7rxE1a8Oh4zpWfZe9oiQUphGtfiGFA6j75UiURH5J3MmGHa/G4t15I3iLLbYtUXxvb1i7evg==", + "requires": { + "@aws-sdk/types": "3.821.0", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, "@aws-sdk/util-locate-window": { "version": "3.804.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz", @@ -1848,13 +2575,6 @@ "@smithy/util-retry": "^4.0.5", "tslib": "^2.6.2", "uuid": "^9.0.1" - }, - "dependencies": { - "uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" - } } }, "@smithy/middleware-serde": { @@ -2138,6 +2858,16 @@ "tslib": "^2.6.2" } }, + "@smithy/util-waiter": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.5.tgz", + "integrity": "sha512-4QvC49HTteI1gfemu0I1syWovJgPvGn7CVUoN9ZFkdvr/cCFkrEL7qNCdx/2eICqDWEGnnr68oMdSIPCLAriSQ==", + "requires": { + "@smithy/abort-controller": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + } + }, "@types/uuid": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", @@ -2170,6 +2900,11 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" } } } diff --git a/private-ca/server/package.json b/private-ca/server/package.json index bd9f7a1..63f47ab 100644 --- a/private-ca/server/package.json +++ b/private-ca/server/package.json @@ -11,6 +11,7 @@ "author": "", "license": "ISC", "dependencies": { + "@aws-sdk/client-ec2": "^3.827.0", "@aws-sdk/client-secrets-manager": "^3.825.0", "node-forge": "^1.3.1" } From 6fbe55e43fe44690cb49610579ee28a4e7e8d50b Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 20 Jun 2025 13:02:15 +0530 Subject: [PATCH 060/146] fix: if check for root privileges for host certificate generation in client script --- private-ca/client/generate-certificate-curl.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index ebd07a0..e582633 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -96,6 +96,10 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then echo -e "\nError: generateHostSSHCert is not allowed in client environment.\nHost certificate generation requires host/server environment.\n" exit 1 fi + if [ "$EUID" -ne 0 ]; then + echo "Run this script with sudo or as root for generating host certificate." + exit 1 + fi if test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub; then # Host SSH Certificate already exists @@ -178,7 +182,6 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then echo "Failed to contact Lambda CA URL. Ensure the URL is correct and the server is running."; exit 1; } - echo "res: $LAMBDA_RESPONSE" ENCODED_CERTIFICATE=$(echo "$LAMBDA_RESPONSE" | jq -er ".certificate") || { echo "Certificate not found in Lambda response. Aborting."; exit 1; From 749622d2960ea4c857ac9a42882116f83d141b4c Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 20 Jun 2025 13:08:26 +0530 Subject: [PATCH 061/146] add update-private-ca-lambda workflow for AWS Lambda updates and remove build-private-ca-on-push workflow --- .../workflows/build-private-ca-on-push.yaml | 26 ---------------- .../workflows/update-private-ca-lambda.yml | 31 +++++++++++++++++++ 2 files changed, 31 insertions(+), 26 deletions(-) delete mode 100644 .github/workflows/build-private-ca-on-push.yaml create mode 100644 .github/workflows/update-private-ca-lambda.yml diff --git a/.github/workflows/build-private-ca-on-push.yaml b/.github/workflows/build-private-ca-on-push.yaml deleted file mode 100644 index 0004263..0000000 --- a/.github/workflows/build-private-ca-on-push.yaml +++ /dev/null @@ -1,26 +0,0 @@ -name: Build Private CA on Push - -on: - push: - paths: - - private-ca/client/Docker/** - - .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/ - [[ "$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 diff --git a/.github/workflows/update-private-ca-lambda.yml b/.github/workflows/update-private-ca-lambda.yml new file mode 100644 index 0000000..6695bb6 --- /dev/null +++ b/.github/workflows/update-private-ca-lambda.yml @@ -0,0 +1,31 @@ +name: Update Private CA Lambda on push + +on: + push: + paths: + - private-ca/server/ + - .github/workflows/update-private-ca-lambda.yml + +permissions: + id-token: write + contents: read + +jobs: + update-lambda: + runs-on: "ubuntu-22.04" + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + aws-region: ${{ secrets.AWS_REGION }} + role-to-assume: ${{ secrets.AWS_SSH_UPDATE_ROLE_ARN }} + role-session-name: UpdatePrivateCALambda + + - name: Run update-server-on-lambda.sh + run: | + cd private-ca/ + bash update-server-on-lambda.sh From bfe98ff9fea967cd98e3de858677aa02cc787312 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 20 Jun 2025 14:03:52 +0530 Subject: [PATCH 062/146] update generate cert using AWS CLI script: remove public IP from cert request payload and improve error handling --- .../client/generate-certificate-aws-cli.sh | 84 ++++++++++++------- .../client/generate-certificate-curl.sh | 2 +- 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index 1972d12..6fb0d1d 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -1,4 +1,7 @@ #!/bin/bash +set -eo pipefail +trap 'echo "Error occurred on line $LINENO. Exiting."; exit 1;' ERR + CA_ACTION=${1:-"generateHostSSHCert"} ENVIRONMENT=${2:-"client"} AWS_PROFILE=${3:-"default"} @@ -8,6 +11,7 @@ SYSTEM_SSH_DIR=${6:-"/etc/ssh"} LAMBDA_REGION=${7:-'us-west-2'} CA_LAMBDA_FUNCTION_NAME=${8:-"privateCA"} AWS_STS_REGION=${9:-"ap-southeast-1"} +AWS_EC2_REGION=${10:-"us-west-2"} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) [[ $? -ne 0 ]] && { echo "Python not installed."; exit 1; } @@ -18,43 +22,35 @@ is_mfa_enabled() { get_aws_credentials() { local method=${1:-"host"} - + local TEMP_CREDS + if [[ $method == "host" ]]; then TOKEN=$(curl -s --max-time 30 -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 120") - + if [[ -z "$TOKEN" ]]; then echo "Failed to fetch EC2 metadata token. Are you running this script on an EC2 instance?" exit 1 fi - + INSTANCE_ROLE_NAME=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/) TEMP_CREDS=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$INSTANCE_ROLE_NAME) - PUBLIC_IP=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-ipv4) - 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") - elif [[ $method == "client" ]]; then if is_mfa_enabled; then CALLER_IDENTITY=$(aws sts get-caller-identity --profile $AWS_PROFILE) [[ $? -ne 0 ]] && { echo "Your AWS credentials have either expired or are invalid. Please check your credentials and try again."; exit 1; } - - TEMP_CREDS=$(get-credentials $AWS_PROFILE) - 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 ".SessionToken") + TEMP_CREDS=$(get-credentials $AWS_PROFILE) else - TEMP_CREDS=$(aws sts get-session-token --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") + TEMP_CREDS=$(aws sts get-session-token --profile $AWS_PROFILE | jq -r ".Credentials") fi else - echo "Invalid environment" - exit 1 + echo "Invalid environment"; exit 1; fi + + 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 ".SessionToken") } # Check for options @@ -82,7 +78,6 @@ while getopts ":h" option; do esac done - # Check for CA Action if [[ $CA_ACTION = "generateClientSSHCert" ]]; then @@ -112,6 +107,10 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then echo -e "\nError: generateHostSSHCert is not allowed in client environment.\nHost certificate generation requires host/server environment.\n" exit 1 fi + if [ "$EUID" -ne 0 ]; then + echo "Run this script with sudo or as root for generating host certificate." + exit 1 + fi if test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub; then # Host SSH Certificate already exists @@ -152,9 +151,18 @@ pip install -q --upgrade boto3 PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) # Auth Headers -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") +output=$($PYTHON_EXEC aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_STS_REGION) || { + echo "Failed to generate auth header. Check aws-auth-header.py script."; + exit 1; +} +auth_header=$(echo "$output" | jq -er ".Authorization") || { + echo "Failed to parse Authorization from auth-header output."; + exit 1; +} +date=$(echo "$output" | jq -er ".Date") || { + echo "Failed to parse Date from auth-header output."; + exit 1; +} INNER_JSON=$(jq -n \ @@ -164,7 +172,7 @@ INNER_JSON=$(jq -n \ --arg certPubkey "$CERT_PUBKEY" \ --arg action "$CA_ACTION" \ --arg awsRegion "$AWS_STS_REGION" \ - --arg publicIp "$PUBLIC_IP" \ + --arg awsEC2Region "$AWS_EC2_REGION" \ '{ auth: { amzDate: $amzDate, @@ -174,7 +182,7 @@ INNER_JSON=$(jq -n \ certPubkey: $certPubkey, action: $action, awsSTSRegion: $awsRegion, - publicIp: $publicIp + awsEC2Region: $awsEC2Region }' | jq -c) # JSON with body as stringified JSON @@ -197,10 +205,16 @@ aws lambda invoke \ --region $LAMBDA_REGION \ $AWS_PROFILE_ARG -response_body=$(cat response.json | jq -r ".body") +response_body=$(cat response.json | jq -r ".body") || { + echo "Failed to parse response body."; + exit 1; +} if [[ $CA_ACTION = "generateClientSSHCert" ]]; then - ENCODED_CERTIFICATE=$(echo $response_body | jq -r ".certificate") + ENCODED_CERTIFICATE=$(echo "$response_body" | jq -er ".certificate") || { + echo "Certificate not found in Lambda response. Aborting."; + exit 1; + } CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) HOST_CA_PUBKEY=$(echo $response_body | jq -r ".\"host_ca.pub\"" | base64 -d) @@ -214,12 +228,20 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then fi elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then - ENCODED_CERTIFICATE=$(echo $response_body | jq -r ".certificate") + ENCODED_CERTIFICATE=$(echo "$response_body" | jq -er ".certificate") || { + echo "Certificate not found in Lambda response. Aborting."; + exit 1; + } CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) USER_CA_PUBKEY=$(echo $response_body | 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" + + if [[ -n "$CERTIFICATE" ]]; then + echo "$CERTIFICATE" > "${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" + echo "Certificate written to ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" + else + echo "Empty certificate received. Not writing to disk." + exit 1 + fi test -f ${SYSTEM_SSH_DIR}/user_ca.pub || echo $USER_CA_PUBKEY > ${SYSTEM_SSH_DIR}/user_ca.pub diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index e582633..8e71cd5 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -49,7 +49,7 @@ get_aws_credentials() { 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 // .SessionToken // .Sessiontoken") + SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".SessionToken") } # Check for options From 20d18410e26209516d007d90acbfe5a6ae523e31 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 20 Jun 2025 14:45:01 +0530 Subject: [PATCH 063/146] update: use UTC timezone for certificate timestamps to avoid comparison issues - Resolves mismatches where ssh-keygen outputs local time but date command uses UTC --- private-ca/client/generate-certificate-aws-cli.sh | 8 ++++---- private-ca/client/generate-certificate-curl.sh | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index 6fb0d1d..0a227f1 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -85,8 +85,8 @@ 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}') + current_timestamp=$(TZ=UTC date -u +"%Y-%m-%dT%H:%M:%S") + certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${USER_SSH_DIR}/id_rsa-cert.pub | awk '/Valid:/{print $NF}') if [[ $certificate_expiration_timestamp > $current_timestamp ]]; then # Certificate is valid @@ -114,8 +114,8 @@ 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}') + current_timestamp=$(TZ=UTC date -u +"%Y-%m-%dT%H:%M:%S") + certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub | awk '/Valid:/{print $NF}') if [[ $certificate_expiration_timestamp > $current_timestamp ]]; then # Certificate is valid diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 8e71cd5..41652e5 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -74,8 +74,8 @@ 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}') + current_timestamp=$(TZ=UTC date -u +"%Y-%m-%dT%H:%M:%S") + certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${USER_SSH_DIR}/id_rsa-cert.pub | awk '/Valid:/{print $NF}') if [[ $certificate_expiration_timestamp > $current_timestamp ]]; then # Certificate is valid @@ -103,8 +103,8 @@ 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}') + current_timestamp=$(TZ=UTC date -u +"%Y-%m-%dT%H:%M:%S") + certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub | awk '/Valid:/{print $NF}') if [[ $certificate_expiration_timestamp > $current_timestamp ]]; then # Certificate is valid From fdf291a1c7377aafd8e80eb017d8935575255f0a Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Mon, 23 Jun 2025 10:56:15 +0530 Subject: [PATCH 064/146] update private CA workflows and improve error messages in certificate generation scripts --- .github/workflows/update-private-ca-lambda.yml | 2 ++ private-ca/client/aws-auth-header.py | 1 - private-ca/client/generate-certificate-aws-cli.sh | 2 +- private-ca/client/generate-certificate-curl.sh | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/update-private-ca-lambda.yml b/.github/workflows/update-private-ca-lambda.yml index 6695bb6..c20d90f 100644 --- a/.github/workflows/update-private-ca-lambda.yml +++ b/.github/workflows/update-private-ca-lambda.yml @@ -2,6 +2,8 @@ name: Update Private CA Lambda on push on: push: + branches: + - main paths: - private-ca/server/ - .github/workflows/update-private-ca-lambda.yml diff --git a/private-ca/client/aws-auth-header.py b/private-ca/client/aws-auth-header.py index 446aca7..4841098 100644 --- a/private-ca/client/aws-auth-header.py +++ b/private-ca/client/aws-auth-header.py @@ -1,6 +1,5 @@ import sys from datetime import datetime -import boto3 from botocore.auth import SigV4Auth from botocore.awsrequest import AWSRequest from botocore.credentials import Credentials diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index 0a227f1..de16b9a 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -14,7 +14,7 @@ AWS_STS_REGION=${9:-"ap-southeast-1"} AWS_EC2_REGION=${10:-"us-west-2"} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) -[[ $? -ne 0 ]] && { echo "Python not installed."; exit 1; } +[[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } is_mfa_enabled() { grep -q 'get-credentials' ${USER_AWS_DIR}/credentials diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 41652e5..6bb02e9 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -13,7 +13,7 @@ AWS_STS_REGION=${8:-"ap-southeast-1"} AWS_EC2_REGION=${9:-"us-west-2"} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) -[[ $? -ne 0 ]] && { echo "Python not installed."; exit 1; } +[[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } is_mfa_enabled() { grep -q 'get-credentials' ${USER_AWS_DIR}/credentials From d7345b5ae06f71e7a5c11524c85dd44679f02be0 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Mon, 23 Jun 2025 11:27:42 +0530 Subject: [PATCH 065/146] update README: merge client and root README files --- private-ca/README.md | 134 ++++++++++++++++++++++++++++- private-ca/client/README.md | 163 ------------------------------------ 2 files changed, 131 insertions(+), 166 deletions(-) delete mode 100644 private-ca/client/README.md diff --git a/private-ca/README.md b/private-ca/README.md index 096810a..6f8c889 100644 --- a/private-ca/README.md +++ b/private-ca/README.md @@ -2,7 +2,7 @@ This project provides a private Certificate Authority (CA) implementation for generating SSH certificates. It allows you to issue certificates for SSH hosts and users for secure communication. -## Installation +## Deployment Deploy the resources by running: @@ -18,8 +18,136 @@ This creates the following resources on AWS: - 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. +Note: Once the lambda is deployed you will need to manually add an environment variable called `AWS_SECRETS_REGION` to store the region in which AWS secrets for privateCA reside. + +## Prerequisites for usage + +### Running via Docker + +- Docker + +### Running directly + +- Python 3 +- Bash +- Dependencies: `curl`, `jq`, `ssh-keygen`, `base64` + +### Running via AWS CLI (Lambda) + +- AWS CLI +- Python 3 +- Access to the Lambda function in the specified region ## Usage -See [client/README.md](client/README.md) for usage. +### Running directly + +#### For client certificates: + +```bash +bash client/generate-certificate-curl.sh generateClientSSHCert client +``` + +#### For host certificates: + +```bash +bash client/generate-certificate-curl.sh generateHostSSHCert host +``` + +**Note:** + +1. Sudo privilege is required for generating host certificates as they need to write to system directories like `/etc/ssh`. +2. The `ENVIRONMENT` (host or client) parameter affects how AWS credentials are retrieved. See [Script Parameters](#script-parameters) for more details. + +### Running via AWS CLI (Lambda) + +The `generate-certificate-aws-cli.sh` script provides an alternative approach to generate certificates. This method uses AWS CLI to invoke a Lambda function rather than making direct HTTP requests. + +#### Usage: + +```bash +bash client/generate-certificate-aws-cli.sh +``` + +### Running via Docker + +1. Build the Docker image: + + ```bash + cd client + docker build -t certificate-generator . + ``` + +2. Run the Docker container with the required volume mounts and parameters: + + ```bash + docker run --rm \ + -v /home/$USER/.ssh:/root/.ssh \ + -v /etc/ssh:/etc/ssh \ + -v /etc/ssl/privateCA:/etc/ssl/privateCA \ + certificate-generator \ + generateHostSSHCert \ + https:/// \ + host \ + default \ + /root/.ssh + ``` + +## Running as a cron job (optional) + +Since certificates need to be renewed periodically, you can set up a cron job to automatically regenerate them. + +Sample script: + +```bash +#!/bin/bash + +# Create the cron job entry +echo "* */1 * * * cd /path/to/private-ca && bash client/generate-certificate-curl.sh generateHostSSHCert https:/// host >> /home/cron.log 2>&1" > /tmp/root_crontab + +# Load into root's crontab +crontab -u root /tmp/root_crontab + +# Optionally start cron service (only if not already running) +systemctl start cron 2>/dev/null || systemctl start crond 2>/dev/null +``` + +## Script Parameters + +Both `generate-certificate-curl.sh` and `generate-certificate-aws-cli.sh` accept several shared and some script-specific parameters. + +| Parameter | Required | Description | Used In Script(s) | Default Value | +| ------------------------- | -------- | ------------------------------------------------------------------------------ | --------------------------------- | ------------------ | +| `CA_ACTION` | Yes | Action to perform: `generateClientSSHCert` or `generateHostSSHCert` | Both | — | +| `CA_URL` | Yes | URL of the Private CA | `generate-certificate-curl.sh` | — | +| `ENVIRONMENT` | No | Machine environment: `"client"` (uses AWS CLI) or `"host"` (uses EC2 metadata) | Both | `client` | +| `AWS_PROFILE` | No | AWS CLI profile name | Both | `default` | +| `USER_SSH_DIR` | No | Path to user's SSH directory | Both | `/home/$USER/.ssh` | +| `USER_AWS_DIR` | No | Path to user's AWS directory | `generate-certificate-curl.sh` | `/home/$USER/.aws` | +| `SYSTEM_SSH_DIR` | No | Path to system SSH directory | Both | `/etc/ssh` | +| `AWS_STS_REGION` | No | AWS region to use for STS operations | Both | `ap-southeast-1` | +| `LAMBDA_REGION` | No | AWS region where the Lambda function is deployed | `generate-certificate-aws-cli.sh` | `us-west-2` | +| `CA_LAMBDA_FUNCTION_NAME` | No | Name of the Lambda function that performs certificate signing | `generate-certificate-aws-cli.sh` | `privateCA` | + +## Important Notes + +- **Certificate Type**: Determined by the `CA_ACTION` parameter (`generateClientSSHCert` or `generateHostSSHCert`) +- **Permissions**: Host certificates require sudo privileges for system directory access + +## Client Environment Limitations + +**Important**: Client environments can only generate client certificates because they don't have a public IP address. + +- **Host Certificate Requirements**: Host certificates require the public IP address as a hostname when issuing the certificate. Due to the absence of a public IP address, client environments cannot generate host certificates +- **Recommendation**: Use client environments exclusively for generating client certificates, and use host environments (such as EC2 instances with public IPs) for generating host certificates + +## Directory Structure + +- `deploy-server-on-lambda.sh`: Script to deploy the Lambda function and related AWS resources +- `update-server-on-lambda.sh`: Script to update the deployed Lambda function +- `client/`: Directory containing client-side tools + - `generate-certificate-curl.sh`: Main script for certificate generation using curl + - `generate-certificate-aws-cli.sh`: Alternative script using AWS CLI + - `aws-auth-header.py`: Python helper for generating AWS authentication headers + - `Dockerfile`: Docker container configuration +- `server/`: Directory containing server-side Lambda function code diff --git a/private-ca/client/README.md b/private-ca/client/README.md deleted file mode 100644 index a226c50..0000000 --- a/private-ca/client/README.md +++ /dev/null @@ -1,163 +0,0 @@ -# PrivateCA - -This project provides tools to generate SSH certificates using a Private Certificate Authority (CA). It supports generating SSH certificates for both hosts and clients. - -## Prerequisites - -### Running via Docker - -- Docker - -### Running directly - -- Python 3 -- Bash -- Dependencies: `curl`, `jq`, `ssh-keygen`, `base64` - -### Running via AWS CLI (Lambda) - -- AWS CLI -- Python 3 -- Access to the Lambda function in the specified region - -## Usage - -### Running directly - -#### For client certificates: - -```bash -bash generate-certificate-curl.sh generateClientSSHCert client -``` - -#### For host certificates: - -```bash -bash generate-certificate-curl.sh generateHostSSHCert host -``` - -**Note:** - -1. Sudo privilege is required for generating host certificates as they need to write to system directories like `/etc/ssh`. -2. The `ENVIRONMENT` (host or client) parameter affects how AWS credentials are retrieved. See [Script Parameters](#script-parameters) for more details. - -### Running via AWS CLI (Lambda) - -The `generate-certificate-aws-cli.sh` script provides an alternative approach to generate certificates. This method uses AWS CLI to invoke a Lambda function rather than making direct HTTP requests. - -#### Usage: - -```bash -bash generate-certificate-aws-cli.sh -``` - -### Running via Docker - -1. Build the Docker image: - - ```bash - docker build -t certificate-generator . - ``` - -2. Run the Docker container with the required volume mounts and parameters: - - ```bash - docker run --rm \ - -v /home/$USER/.ssh:/root/.ssh \ - -v /etc/ssh:/etc/ssh \ - -v /etc/ssl/privateCA:/etc/ssl/privateCA \ - certificate-generator \ - generateHostSSHCert \ - https:/// \ - host \ - default \ - /root/.ssh - ``` - -## Running as a cron job (optional) - -Since certificates need to be renewed periodically, you can set up a cron job to automatically regenerate them. - -Sample script: - -```bash -#!/bin/bash - -# Create the cron job entry -echo "* */1 * * * cd bash generate-certificate-curl.sh generateHostSSHCert https:/// host >> /home/cron.log 2>&1" > /tmp/root_crontab - -# Load into root's crontab -crontab -u root /tmp/root_crontab - -# Optionally start cron service (only if not already running) -systemctl start cron 2>/dev/null || systemctl start crond 2>/dev/null -``` - -## Script Parameters - -The `generate-certificate-curl.sh` script accepts the following parameters: - -1. **CA_ACTION** (required): The action to perform - - - `generateClientSSHCert`: Generates an SSH certificate for clients - - `generateHostSSHCert`: Generates an SSH certificate for hosts - -2. **CA_URL** (required): URL of the Private CA - -3. **ENVIRONMENT** (optional): Machine environment type - "client" or "host" (defaults to "client") - - - **"host"**: For EC2 instances - uses EC2 instance metadata for AWS credentials - - **"client"**: For local user machines - uses AWS CLI/STS for credentials - -4. **AWS_PROFILE** (optional): AWS profile name (defaults to "default") - -5. **USER_SSH_DIR** (optional): Path to user's SSH directory (defaults to "/home/$USER/.ssh") - -6. **USER_AWS_DIR** (optional): Path to user's AWS directory (defaults to "/home/$USER/.aws") - -7. **SYSTEM_SSH_DIR** (optional): Path to system SSH directory (defaults to "/etc/ssh") - -8. **AWS_STS_REGION** (optional): AWS region for STS (defaults to "ap-southeast-1") - -The `generate-certificate-aws-cli.sh` script accepts the following parameters: - -1. **CA_ACTION** (required): The action to perform - - - `generateClientSSHCert`: Generates an SSH certificate for clients - - `generateHostSSHCert`: Generates an SSH certificate for hosts - -2. **ENVIRONMENT** (optional): Machine environment type - "client" or "host" (defaults to "client") - - - **"host"**: For EC2 instances - uses EC2 instance metadata for AWS credentials - - **"client"**: For local user machines - uses AWS CLI/STS for credentials - -3. **AWS_PROFILE** (optional): AWS profile name (defaults to "default") - -4. **USER_SSH_DIR** (optional): Path to user's SSH directory (defaults to "/home/$USER/.ssh") - -5. **SYSTEM_SSH_DIR** (optional): Path to system SSH directory (defaults to "/etc/ssh") - -6. **LAMBDA_REGION** (optional): AWS region where the Lambda function is deployed (defaults to "us-west-2") - -7. **CA_LAMBDA_FUNCTION_NAME** (optional): Name of the Lambda function (defaults to "privateCA") - -8. **AWS_STS_REGION** (optional): AWS region for STS operations (defaults to "ap-southeast-1") - -## Important Notes - -- **Certificate Type**: Determined by the `CA_ACTION` parameter (`generateClientSSHCert` or `generateHostSSHCert`) -- **Permissions**: Host certificates require sudo privileges for system directory access - -## Client Environment Limitations - -**Important**: Client environments can only generate client certificates because they don't have a public IP address. - -- **Host Certificate Requirements**: Host certificates require the public IP address as a hostname when issuing the certificate. Due to the absence of a public IP address, client environments cannot generate host certificates -- **Recommendation**: Use client environments exclusively for generating client certificates, and use host environments (such as EC2 instances with public IPs) for generating host certificates - -## Directory Structure - -- `generate-certificate-curl.sh`: Main script for certificate generation using curl -- `generate-certificate-aws-cli.sh`: Alternative script using AWS CLI -- `aws-auth-header.py`: Python helper for generating AWS authentication headers -- `Dockerfile`: Docker container configuration From 1f162ee912f250a82bd3a3187a496c1552a02a43 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Mon, 23 Jun 2025 13:02:51 +0530 Subject: [PATCH 066/146] update timezone in aws-auth-header: use UTC, improve error messages for environment validation, and fix session token retrieval logic --- private-ca/client/aws-auth-header.py | 4 ++-- private-ca/client/generate-certificate-aws-cli.sh | 13 ++++++------- private-ca/client/generate-certificate-curl.sh | 4 ++-- private-ca/server/index_lambda.js | 4 ++-- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/private-ca/client/aws-auth-header.py b/private-ca/client/aws-auth-header.py index 4841098..566e08a 100644 --- a/private-ca/client/aws-auth-header.py +++ b/private-ca/client/aws-auth-header.py @@ -1,5 +1,5 @@ import sys -from datetime import datetime +from datetime import datetime, timezone from botocore.auth import SigV4Auth from botocore.awsrequest import AWSRequest from botocore.credentials import Credentials @@ -14,7 +14,7 @@ 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(timezone.utc).strftime('%Y%m%dT%H%M%SZ'), 'Aud': 'FundwaveCA' } request = AWSRequest(method="POST", url="/", data=request_parameters, headers=request_headers) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index de16b9a..ed4c3ad 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -45,12 +45,12 @@ get_aws_credentials() { TEMP_CREDS=$(aws sts get-session-token --profile $AWS_PROFILE | jq -r ".Credentials") fi else - echo "Invalid environment"; exit 1; + echo "echo "Invalid environment provided. Allowed values are 'host' and 'client'"; exit 1; t"; exit 1; fi 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 ".SessionToken") + SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".Token // .SessionToken // .Sessiontoken") } # Check for options @@ -59,18 +59,17 @@ while getopts ":h" option; do h) echo "Usage: bash generate-certificate-aws-cli.sh [ACTION] [ENVIRONMENT] [AWS PROFILE] [USER SSH DIR] [USER AWS DIR] [SYSTEM SSH DIR] [AWS STS REGION]" echo "" + echo "Actions:" + echo " generateHostSSHCert Generates SSH Certificate for Host" + echo " generateClientSSHCert Generates SSH Certificate for Client" + echo "" echo "Parameters:" - echo " CA_ACTION Action to perform (default: generateHostSSHCert)" echo " ENVIRONMENT Environment to use (default: client)" echo " AWS PROFILE AWS profile to use (default: default)" echo " USER SSH DIR Path to user's SSH directory (default: /home/$USER/.ssh)" echo " USER AWS DIR Path to user's AWS directory (default: /home/$USER/.aws)" echo " SYSTEM SSH DIR Path to system's SSH directory (default: /etc/ssh)" echo " AWS STS REGION AWS region for STS operations (default: ap-southeast-1)" - echo "" - echo "CA_ACTION:" - echo " generateHostSSHCert Generates SSH Certificate for Host" - echo " generateClientSSHCert Generates SSH Certificate for Client" exit;; *) echo "Error: Invalid option" diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 6bb02e9..33f372e 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -44,12 +44,12 @@ get_aws_credentials() { TEMP_CREDS=$(aws sts get-session-token --profile $AWS_PROFILE | jq -r ".Credentials") fi else - echo "Invalid environment"; exit 1; + echo "Invalid environment provided. Allowed values are 'host' and 'client'"; exit 1; fi 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 ".SessionToken") + SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".Token // .SessionToken // .Sessiontoken") } # Check for options diff --git a/private-ca/server/index_lambda.js b/private-ca/server/index_lambda.js index 1aca1cc..71bfd90 100644 --- a/private-ca/server/index_lambda.js +++ b/private-ca/server/index_lambda.js @@ -2,7 +2,7 @@ import { signHostSSHCertificate } from './generate-host-ssh-cert.js'; import { signClientSSHCertificate } from './generate-client-ssh-cert.js'; import { getCallerIdentity } from './get-caller-identity.js'; import { getSecret } from './secret-manager-utils.js'; -const AWS_SCRTS_REGION = process.env.AWS_SCRTS_REGION; +const AWS_SECRETS_REGION = process.env.AWS_SECRETS_REGION; export const handler = async (event) => { @@ -12,7 +12,7 @@ export const handler = async (event) => { const callerIdentity = await getCallerIdentity(event); // secret - const secret = await getSecret(AWS_SCRTS_REGION, 'privateCA'); + const secret = await getSecret(AWS_SECRETS_REGION, 'privateCA'); // action switch(event.action) { From 2331c14a7bedd9a3467634cf672f9c2d4cf78a95 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Mon, 23 Jun 2025 14:09:29 +0530 Subject: [PATCH 067/146] update README and Dockerfile: remove unnecessary dependencies --- private-ca/README.md | 2 +- private-ca/client/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/private-ca/README.md b/private-ca/README.md index 6f8c889..d53e812 100644 --- a/private-ca/README.md +++ b/private-ca/README.md @@ -22,7 +22,7 @@ Note: Once the lambda is deployed you will need to manually add an environment v ## Prerequisites for usage -### Running via Docker +### Running via Docker (for host machines only) - Docker diff --git a/private-ca/client/Dockerfile b/private-ca/client/Dockerfile index 444cf5a..15b5459 100644 --- a/private-ca/client/Dockerfile +++ b/private-ca/client/Dockerfile @@ -1,6 +1,6 @@ FROM alpine:3.18.3 -RUN apk add bash curl unzip groff jq python3 openssh openssl +RUN apk add bash curl jq python3 openssh WORKDIR /app From 1235a144a824d0ce74a29c9acd4403cbef83851a Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Mon, 23 Jun 2025 14:49:55 +0530 Subject: [PATCH 068/146] update update-private-ca-lambda workflow: upgrade AWS credentials action from v2 to v4 --- .github/workflows/update-private-ca-lambda.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-private-ca-lambda.yml b/.github/workflows/update-private-ca-lambda.yml index c20d90f..1737831 100644 --- a/.github/workflows/update-private-ca-lambda.yml +++ b/.github/workflows/update-private-ca-lambda.yml @@ -21,7 +21,7 @@ jobs: uses: actions/checkout@v3 - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v2 + uses: aws-actions/configure-aws-credentials@v4 with: aws-region: ${{ secrets.AWS_REGION }} role-to-assume: ${{ secrets.AWS_SSH_UPDATE_ROLE_ARN }} From abfd4427c0e0ea6055385740d0847f8f1e65cf18 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Mon, 23 Jun 2025 16:15:39 +0530 Subject: [PATCH 069/146] fix generate-certificate-aws-cli script: correct error message formatting for invalid environment input --- private-ca/client/generate-certificate-aws-cli.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index ed4c3ad..2faf3f6 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -45,7 +45,7 @@ get_aws_credentials() { TEMP_CREDS=$(aws sts get-session-token --profile $AWS_PROFILE | jq -r ".Credentials") fi else - echo "echo "Invalid environment provided. Allowed values are 'host' and 'client'"; exit 1; t"; exit 1; + echo "Invalid environment provided. Allowed values are 'host' and 'client'"; exit 1; fi ACCESS_KEY_ID=$(echo $TEMP_CREDS | jq -r ".AccessKeyId") From 34a2cd146dea06876c902f6a2732074a6d7a55f2 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 24 Jun 2025 10:21:21 +0530 Subject: [PATCH 070/146] update private-ca workflow: modify path to include all subdirectories under private-ca/server --- .github/workflows/update-private-ca-lambda.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-private-ca-lambda.yml b/.github/workflows/update-private-ca-lambda.yml index 1737831..6787a12 100644 --- a/.github/workflows/update-private-ca-lambda.yml +++ b/.github/workflows/update-private-ca-lambda.yml @@ -5,7 +5,7 @@ on: branches: - main paths: - - private-ca/server/ + - private-ca/server/** - .github/workflows/update-private-ca-lambda.yml permissions: From ebd237652d662a1e35ed8c0c5c67d6baace0c34b Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 24 Jun 2025 10:30:16 +0530 Subject: [PATCH 071/146] rename private CA client venv to 'private-ca-client-env' --- private-ca/client/generate-certificate-aws-cli.sh | 6 +++--- private-ca/client/generate-certificate-curl.sh | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index 2faf3f6..a5917f1 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -139,10 +139,10 @@ fi # Temporary Credentials get_aws_credentials $ENVIRONMENT -if [ ! -d "env" ]; then - $PYTHON_EXEC -m venv env +if [ ! -d "private-ca-client-env" ]; then + $PYTHON_EXEC -m venv private-ca-client-env fi -source env/bin/activate +source private-ca-client-env/bin/activate pip install -q --upgrade boto3 # Update PYTHON_EXEC to use the Python executable from the activated virtual environment diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 33f372e..60482e3 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -128,10 +128,10 @@ fi get_aws_credentials $ENVIRONMENT -if [ ! -d "env" ]; then - $PYTHON_EXEC -m venv env +if [ ! -d "private-ca-client-env" ]; then + $PYTHON_EXEC -m venv private-ca-client-env fi -source env/bin/activate +source private-ca-client-env/bin/activate pip install -q --upgrade boto3 # Update PYTHON_EXEC to use the Python executable from the activated virtual environment From 1c6fa9dbaf4fdb07d9c136b4b4eb912b01f6f195 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 24 Jun 2025 11:07:46 +0530 Subject: [PATCH 072/146] chore: document logic for adding host CA public key to known_hosts --- private-ca/client/generate-certificate-aws-cli.sh | 4 ++++ private-ca/client/generate-certificate-curl.sh | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index a5917f1..c3ae416 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -222,7 +222,11 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then [[ -f "${USER_SSH_DIR}/known_hosts" ]] || touch "${USER_SSH_DIR}/known_hosts" + # Add host CA public key to known_hosts file if it doesn't exist if [[ $(grep -q "@cert-authority" "${USER_SSH_DIR}/known_hosts"; echo $?) -ne 0 ]]; then + # @cert-authority tells ssh to trust the host CA public key + # * means all hosts (wildcard) (you can also specify a list of comma separated hostnames) + # ${HOST_CA_PUBKEY} is the host CA public key that was used to sign the host certificate echo "@cert-authority * ${HOST_CA_PUBKEY}" >> ${USER_SSH_DIR}/known_hosts fi diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 60482e3..339a303 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -172,7 +172,11 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then [[ -f "${USER_SSH_DIR}/known_hosts" ]] || touch "${USER_SSH_DIR}/known_hosts" + # Add host CA public key to known_hosts file if it doesn't exist if [[ $(grep -q "@cert-authority" "${USER_SSH_DIR}/known_hosts"; echo $?) -ne 0 ]]; then + # @cert-authority tells ssh to trust the host CA public key + # * means all hosts (wildcard) (you can also specify a list of comma separated hostnames) + # ${HOST_CA_PUBKEY} is the host CA public key that was used to sign the host certificate echo "@cert-authority * ${HOST_CA_PUBKEY}" >> ${USER_SSH_DIR}/known_hosts fi From 02343fbb7e228401f96d690f97c3585a7aaebf89 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 24 Jun 2025 11:10:02 +0530 Subject: [PATCH 073/146] refactor: simplify getPublicIpAddress function signature --- private-ca/server/generate-host-ssh-cert.js | 5 +---- private-ca/server/get-public-ip-address.js | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/private-ca/server/generate-host-ssh-cert.js b/private-ca/server/generate-host-ssh-cert.js index bfcc917..e38c0e1 100644 --- a/private-ca/server/generate-host-ssh-cert.js +++ b/private-ca/server/generate-host-ssh-cert.js @@ -10,10 +10,7 @@ export const signHostSSHCertificate = async (callerIdentity, secret, certPubkey, const arn = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn; const instanceId = arn.match(/\/([^/]+)$/)?.[1]; - const publicIp = await getPublicIpAddress({ - awsEC2Region: awsEC2Region, - instanceId: instanceId - }); + const publicIp = await getPublicIpAddress(awsEC2Region, instanceId); const caKeyPath = "/tmp/host_ca"; const publicKeyName = "ssh_host_rsa_key"; diff --git a/private-ca/server/get-public-ip-address.js b/private-ca/server/get-public-ip-address.js index 1c8afa5..a140fa9 100644 --- a/private-ca/server/get-public-ip-address.js +++ b/private-ca/server/get-public-ip-address.js @@ -1,9 +1,6 @@ import { EC2Client, DescribeInstancesCommand } from "@aws-sdk/client-ec2"; -export const getPublicIpAddress = async (event) => { - const region = event.awsEC2Region; - const instanceId = event.instanceId; - +export const getPublicIpAddress = async (region, instanceId) => { const client = new EC2Client({ region }); const command = new DescribeInstancesCommand({ From aa3937cc9f1a2c03be48e5cf0c1bf7ecf83db72f Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 24 Jun 2025 11:31:41 +0530 Subject: [PATCH 074/146] quiet zip output to keep logs clean --- private-ca/deploy-server-on-lambda.sh | 4 ++-- private-ca/update-server-on-lambda.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/private-ca/deploy-server-on-lambda.sh b/private-ca/deploy-server-on-lambda.sh index 23b87dc..3ace825 100755 --- a/private-ca/deploy-server-on-lambda.sh +++ b/private-ca/deploy-server-on-lambda.sh @@ -67,7 +67,7 @@ rm Trust-Policy.json Policy.json # 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 +sudo zip -qry ./openssh-layer.zip . > /dev/null LAYER_ARN=$(aws lambda publish-layer-version \ --layer-name $LAYER_NAME \ --zip-file fileb://openssh-layer.zip \ @@ -86,7 +86,7 @@ sudo rm -r openssh-layer/ # Create lambda function cd server npm i -zip -r ./lambda.zip . +zip -qr ./lambda.zip . mv lambda.zip ../ cd .. diff --git a/private-ca/update-server-on-lambda.sh b/private-ca/update-server-on-lambda.sh index 7bc60ca..a87ace3 100755 --- a/private-ca/update-server-on-lambda.sh +++ b/private-ca/update-server-on-lambda.sh @@ -4,7 +4,7 @@ PROFILE=${3:-'default'} cd server npm i -zip -r ./lambda.zip . +zip -qr ./lambda.zip . mv lambda.zip ../ cd .. From 27d9e525a5b228e59509a59c603a43d96dd4b391 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 24 Jun 2025 12:40:56 +0530 Subject: [PATCH 075/146] add error handling and validation in private CA lambda --- private-ca/server/index_lambda.js | 76 ++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/private-ca/server/index_lambda.js b/private-ca/server/index_lambda.js index 71bfd90..c85cdf9 100644 --- a/private-ca/server/index_lambda.js +++ b/private-ca/server/index_lambda.js @@ -2,37 +2,59 @@ import { signHostSSHCertificate } from './generate-host-ssh-cert.js'; import { signClientSSHCertificate } from './generate-client-ssh-cert.js'; import { getCallerIdentity } from './get-caller-identity.js'; import { getSecret } from './secret-manager-utils.js'; + const AWS_SECRETS_REGION = process.env.AWS_SECRETS_REGION; export const handler = async (event) => { - - event=JSON.parse(event.body); - - // auth - const callerIdentity = await getCallerIdentity(event); + try { + event = JSON.parse(event.body); - // secret - const secret = await getSecret(AWS_SECRETS_REGION, 'privateCA'); - - // action - switch(event.action) { - case "generateHostSSHCert": - const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.certPubkey, event.awsEC2Region); - return { - statusCode: 200, - 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: "{\"certificate\" : \""+Buffer.from(clientSSHCert).toString('base64')+"\", \"host_ca.pub\": \""+secret["host_ca.pub"]+"\"}" - }; - default: - console.log("Invalid Action") + if (!event.certPubkey) { return { statusCode: 400, - body: JSON.stringify('Invalid Action') + body: JSON.stringify({ error: 'Missing certPubkey' }), }; - } -}; + } + + // auth + const callerIdentity = await getCallerIdentity(event); + + // secret + const secret = await getSecret(AWS_SECRETS_REGION, 'privateCA'); + + // action + switch (event.action) { + case "generateHostSSHCert": + if (!event.awsEC2Region) { + return { + statusCode: 400, + body: JSON.stringify({ error: 'Missing awsEC2Region' }), + }; + } + + const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.certPubkey, event.awsEC2Region); + return { + statusCode: 200, + 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: "{\"certificate\" : \""+Buffer.from(clientSSHCert).toString('base64')+"\", \"host_ca.pub\": \""+secret["host_ca.pub"]+"\"}" + }; + default: + console.log("Invalid Action") + return { + statusCode: 400, + body: JSON.stringify({ error: 'Invalid Action' }), + }; + } + } catch (err) { + console.error('Error in cert signing handler:', err); + return { + statusCode: 500, + body: JSON.stringify({ error: 'Internal server error' }), + }; + } +}; \ No newline at end of file From 7959f05140c8a57dbd88051c0a72ec525fe5780c Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 24 Jun 2025 13:54:43 +0530 Subject: [PATCH 076/146] restore non-related file changes to match main branch version --- trusted-fingerprint/client/verify-fingerprint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trusted-fingerprint/client/verify-fingerprint.sh b/trusted-fingerprint/client/verify-fingerprint.sh index d71daa1..56150b5 100755 --- a/trusted-fingerprint/client/verify-fingerprint.sh +++ b/trusted-fingerprint/client/verify-fingerprint.sh @@ -14,7 +14,7 @@ hashed_hostname=$HOST keyscan_output=$(ssh-keyscan -T $KEYSCAN_TIMEOUT -t $USER_KEY_TYPE $HOST | grep -v "^#") [[ "$hash_known_hosts" == "yes" ]] && hashed_hostname=$(echo "$keyscan_output" | awk '{print $1}') -host_key=$(echo "$keyscan_output" | grep -v "^#" | awk '{print $3}') +host_key=$(echo "$keyscan_output" | awk '{print $3}') # Check if the key is empty if [[ -z "$host_key" ]]; then From e9b981ae63a4cb6aa73b33724a12abfd750fb7fb Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 24 Jun 2025 13:58:00 +0530 Subject: [PATCH 077/146] refactor: use JSON.stringify for certificate bodies --- private-ca/server/index_lambda.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/private-ca/server/index_lambda.js b/private-ca/server/index_lambda.js index c85cdf9..456f3b8 100644 --- a/private-ca/server/index_lambda.js +++ b/private-ca/server/index_lambda.js @@ -35,13 +35,19 @@ export const handler = async (event) => { const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.certPubkey, event.awsEC2Region); return { statusCode: 200, - body: "{\"certificate\" : \""+Buffer.from(hostSSHCert).toString('base64')+"\", \"user_ca.pub\": \""+secret["user_ca.pub"]+"\"}" + body: JSON.stringify({ + "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: "{\"certificate\" : \""+Buffer.from(clientSSHCert).toString('base64')+"\", \"host_ca.pub\": \""+secret["host_ca.pub"]+"\"}" + body: JSON.stringify({ + "certificate": Buffer.from(clientSSHCert).toString('base64'), + "host_ca.pub": secret["host_ca.pub"] + }) }; default: console.log("Invalid Action") From 638733832783330b61d6f5361cd2d6c354004afd Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 24 Jun 2025 13:59:29 +0530 Subject: [PATCH 078/146] refactor: remove commented-out updateSecret function from secret-manager-utils --- private-ca/server/secret-manager-utils.js | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/private-ca/server/secret-manager-utils.js b/private-ca/server/secret-manager-utils.js index 2566c86..a2e4804 100644 --- a/private-ca/server/secret-manager-utils.js +++ b/private-ca/server/secret-manager-utils.js @@ -6,19 +6,4 @@ export const getSecret = async (secretRegion, secretId) => { const response = await client.send(command); const secret = JSON.parse(response.SecretString); return secret; -} - - -/** -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 = { - SecretId: secretId, - SecretString: JSON.stringify(secret) - }; - const updateRes = await secretsmanager.updateSecret(params).promise(); - return updateRes; -} -**/ +} \ No newline at end of file From ebf6fd204ae730a5a098c171d162e65af872d158 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 24 Jun 2025 14:15:35 +0530 Subject: [PATCH 079/146] fix: return exit code 1 in certificate generation scripts in case of invalid action --- private-ca/client/generate-certificate-aws-cli.sh | 2 +- private-ca/client/generate-certificate-curl.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index c3ae416..a27fc91 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -133,7 +133,7 @@ else echo "Possible actions include:" echo " generateHostSSHCert: Generates SSH Certificate for Host" echo " generateClientSSHCert: Generates SSH Certificate for Client" - exit; + exit 1; fi # Temporary Credentials diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 339a303..63eb954 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -123,7 +123,7 @@ else echo "Possible actions include:" echo " generateHostSSHCert: Generates SSH Certificate for Host" echo " generateClientSSHCert: Generates SSH Certificate for Client" - exit; + exit 1; fi get_aws_credentials $ENVIRONMENT From 458dde1d6c3870c8a2cb0aa38dac87514aa9b5cd Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 24 Jun 2025 14:17:54 +0530 Subject: [PATCH 080/146] chore: use Ubuntu 24.04 for the update-private-ca-lambda job --- .github/workflows/update-private-ca-lambda.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-private-ca-lambda.yml b/.github/workflows/update-private-ca-lambda.yml index 6787a12..dffd04d 100644 --- a/.github/workflows/update-private-ca-lambda.yml +++ b/.github/workflows/update-private-ca-lambda.yml @@ -14,7 +14,7 @@ permissions: jobs: update-lambda: - runs-on: "ubuntu-22.04" + runs-on: "ubuntu-24.04" steps: - name: Checkout code From 31196563bc148456c1271ffdb4dca80cff3de006 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 24 Jun 2025 16:40:22 +0530 Subject: [PATCH 081/146] fix: redirect output of update-server-on-lambda script to suppress logs --- private-ca/update-server-on-lambda.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/private-ca/update-server-on-lambda.sh b/private-ca/update-server-on-lambda.sh index a87ace3..b5fb6a7 100755 --- a/private-ca/update-server-on-lambda.sh +++ b/private-ca/update-server-on-lambda.sh @@ -10,6 +10,6 @@ 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 + --zip-file fileb://lambda.zip --region $REGION --profile $PROFILE >/dev/null 2>&1 rm -r lambda.zip \ No newline at end of file From 943908b84a3e9a1e1a11d051c7c1341eafc26c41 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 24 Jun 2025 18:10:09 +0530 Subject: [PATCH 082/146] refactor: remove unused SSL key and certificate generation from deploy-server-on-lambda script --- private-ca/deploy-server-on-lambda.sh | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/private-ca/deploy-server-on-lambda.sh b/private-ca/deploy-server-on-lambda.sh index 3ace825..2d1795c 100755 --- a/private-ca/deploy-server-on-lambda.sh +++ b/private-ca/deploy-server-on-lambda.sh @@ -13,19 +13,12 @@ AWS_PROFILE=${7:-"default"} 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 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 | 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 +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}\"}" | jq . > secret.json # Create Secret SECRET_ARN=$(aws secretsmanager create-secret \ @@ -36,7 +29,7 @@ SECRET_ARN=$(aws secretsmanager create-secret \ | 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 +rm host_ca host_ca.pub user_ca user_ca.pub secret.json ############################################ ################### Role ################### From 57a0c8ce09145955e5e27dde9c01e68664d7fa75 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 25 Jun 2025 10:45:42 +0530 Subject: [PATCH 083/146] build: use esbuild for bundling and related changes in deployment scripts --- private-ca/deploy-server-on-lambda.sh | 5 +- private-ca/server/build.mjs | 11 + private-ca/server/package-lock.json | 2562 ++++++++----------------- private-ca/server/package.json | 10 +- private-ca/update-server-on-lambda.sh | 3 +- 5 files changed, 776 insertions(+), 1815 deletions(-) create mode 100644 private-ca/server/build.mjs diff --git a/private-ca/deploy-server-on-lambda.sh b/private-ca/deploy-server-on-lambda.sh index 2d1795c..979c50e 100755 --- a/private-ca/deploy-server-on-lambda.sh +++ b/private-ca/deploy-server-on-lambda.sh @@ -79,7 +79,8 @@ sudo rm -r openssh-layer/ # Create lambda function cd server npm i -zip -qr ./lambda.zip . +npm run build +cd dist && zip -qr ../lambda.zip . && cd .. mv lambda.zip ../ cd .. @@ -102,7 +103,7 @@ aws lambda add-permission \ --region $AWS_REGION \ --profile $AWS_PROFILE -FUNCTION_URL=$(aws lambda create-function-url-config --function-name "privateCA" --auth-type "NONE" --region $AWS_REGION --profile $AWS_PROFILE | jq -r ".FunctionUrl") +FUNCTION_URL=$(aws lambda create-function-url-config --function-name "$FUNCTION_NAME" --auth-type "NONE" --region $AWS_REGION --profile $AWS_PROFILE | jq -r ".FunctionUrl") echo "CA deployed at URL:" echo "${FUNCTION_URL}" diff --git a/private-ca/server/build.mjs b/private-ca/server/build.mjs new file mode 100644 index 0000000..d96439e --- /dev/null +++ b/private-ca/server/build.mjs @@ -0,0 +1,11 @@ +import { build } from 'esbuild'; + +await build({ + entryPoints: ['index_lambda.js'], + bundle: true, + platform: 'node', + target: 'node18', + outfile: 'dist/index_lambda.js', + format: 'esm', + external: [], +}); \ No newline at end of file diff --git a/private-ca/server/package-lock.json b/private-ca/server/package-lock.json index 120901e..b06d133 100644 --- a/private-ca/server/package-lock.json +++ b/private-ca/server/package-lock.json @@ -1,7 +1,7 @@ { "name": "private-ca", "version": "0.0.1", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -12,12 +12,16 @@ "@aws-sdk/client-ec2": "^3.827.0", "@aws-sdk/client-secrets-manager": "^3.825.0", "node-forge": "^1.3.1" + }, + "devDependencies": { + "esbuild": "^0.25.5" } }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", @@ -32,6 +36,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -43,6 +48,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -55,6 +61,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -67,6 +74,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", @@ -80,6 +88,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" } @@ -88,6 +97,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", @@ -98,6 +108,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -109,6 +120,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -121,6 +133,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -130,48 +143,49 @@ } }, "node_modules/@aws-sdk/client-ec2": { - "version": "3.827.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-ec2/-/client-ec2-3.827.0.tgz", - "integrity": "sha512-vOALW4vUcsk9+dlNIgyyN5msKpy7TN+TxkFeHwVQOXPzkL5/0V+x1UWxxmUKnhOXxfG2JK3KD9+A9S9IkMHCKQ==", + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-ec2/-/client-ec2-3.835.0.tgz", + "integrity": "sha512-dqJQ1ju8FBB/v26S5UjirvHbvdOE2uA4x/AVyUa9KClYzKGE3/5Jiz7RtKlBT+1eTJEV9oU6/jYCI45M8Waaug==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.826.0", - "@aws-sdk/credential-provider-node": "3.826.0", + "@aws-sdk/core": "3.835.0", + "@aws-sdk/credential-provider-node": "3.835.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-sdk-ec2": "3.826.0", - "@aws-sdk/middleware-user-agent": "3.826.0", + "@aws-sdk/middleware-sdk-ec2": "3.835.0", + "@aws-sdk/middleware-user-agent": "3.835.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.826.0", + "@aws-sdk/util-user-agent-node": "3.835.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.11", - "@smithy/middleware-retry": "^4.1.12", + "@smithy/middleware-endpoint": "^4.1.12", + "@smithy/middleware-retry": "^4.1.13", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.3", + "@smithy/smithy-client": "^4.4.4", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.19", - "@smithy/util-defaults-mode-node": "^4.0.19", + "@smithy/util-defaults-mode-browser": "^4.0.20", + "@smithy/util-defaults-mode-node": "^4.0.20", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.5", + "@smithy/util-retry": "^4.0.6", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.5", "@types/uuid": "^9.0.1", @@ -182,356 +196,49 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sso": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.826.0.tgz", - "integrity": "sha512-/FEKnUC3xPkLL4RuRydwzx+y4b55HIX6qLPbGnyIs+sNmCUyc/62ijtV1Ml+b++YzEF6jWNBsJOxeyZdgrJ3Ig==", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.826.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.826.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.826.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.11", - "@smithy/middleware-retry": "^4.1.12", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.3", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.19", - "@smithy/util-defaults-mode-node": "^4.0.19", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.5", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/core": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.826.0.tgz", - "integrity": "sha512-BGbQYzWj3ps+dblq33FY5tz/SsgJCcXX0zjQlSC07tYvU1jHTUvsefphyig+fY38xZ4wdKjbTop+KUmXUYrOXw==", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@aws-sdk/xml-builder": "3.821.0", - "@smithy/core": "^3.5.3", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.3", - "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-utf8": "^4.0.0", - "fast-xml-parser": "4.4.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.826.0.tgz", - "integrity": "sha512-DK3pQY8+iKK3MGDdC3uOZQ2psU01obaKlTYhEwNu4VWzgwQL4Vi3sWj4xSWGEK41vqZxiRLq6fOq7ysRI+qEZA==", - "dependencies": { - "@aws-sdk/core": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.826.0.tgz", - "integrity": "sha512-N+IVZBh+yx/9GbMZTKO/gErBi/FYZQtcFRItoLbY+6WU+0cSWyZYfkoeOxHmQV3iX9k65oljERIWUmL9x6OSQg==", - "dependencies": { - "@aws-sdk/core": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.3", - "@smithy/types": "^4.3.1", - "@smithy/util-stream": "^4.2.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.826.0.tgz", - "integrity": "sha512-g7n+qSklq/Lzjxe2Ke5QFNCgYn26a3ydZnbFIk8QqYin4pzG+qiunaqJjpV3c/EeHMlfK8bBc7MXAylKzGRccQ==", - "dependencies": { - "@aws-sdk/core": "3.826.0", - "@aws-sdk/credential-provider-env": "3.826.0", - "@aws-sdk/credential-provider-http": "3.826.0", - "@aws-sdk/credential-provider-process": "3.826.0", - "@aws-sdk/credential-provider-sso": "3.826.0", - "@aws-sdk/credential-provider-web-identity": "3.826.0", - "@aws-sdk/nested-clients": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.826.0.tgz", - "integrity": "sha512-UfIJXxHjmSxH6bea00HBPLkjNI2D04enQA/xNLZvB+4xtzt1/gYdCis1P4/73f5aGVVVB4/zQMobBbnjkrmbQw==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.826.0", - "@aws-sdk/credential-provider-http": "3.826.0", - "@aws-sdk/credential-provider-ini": "3.826.0", - "@aws-sdk/credential-provider-process": "3.826.0", - "@aws-sdk/credential-provider-sso": "3.826.0", - "@aws-sdk/credential-provider-web-identity": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.826.0.tgz", - "integrity": "sha512-kURrc4amu3NLtw1yZw7EoLNEVhmOMRUTs+chaNcmS+ERm3yK0nKjaJzmKahmwlTQTSl3wJ8jjK7x962VPo+zWw==", - "dependencies": { - "@aws-sdk/core": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.826.0.tgz", - "integrity": "sha512-F19J3zcfoom6OnQ0MyAtvduVKQXPgkz9i5ExSO01J2CzjbyMhCDA99qAjHYe+LwhW+W7P/jzBPd0+uOQ2Nhh9Q==", - "dependencies": { - "@aws-sdk/client-sso": "3.826.0", - "@aws-sdk/core": "3.826.0", - "@aws-sdk/token-providers": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.826.0.tgz", - "integrity": "sha512-o27GZ6Hy7qhuvMFVUL2eFEpBzf33Jaa/x3u3SHwU0nL7ko7jmbpeF0x4+wmagpI9X2IvVlUxIs0VaQ3YayPLEA==", - "dependencies": { - "@aws-sdk/core": "3.826.0", - "@aws-sdk/nested-clients": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.826.0.tgz", - "integrity": "sha512-j404+EcfBbtTlAhyObjXbdKwwDXO1pCxHvR5Fw8FXNvp/H330j6YnXgs3SJ6d3bZUwUJ/ztPx2S5AlBbLVLDFw==", - "dependencies": { - "@aws-sdk/core": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", - "@smithy/core": "^3.5.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/nested-clients": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.826.0.tgz", - "integrity": "sha512-p7olPq0uTtHqGuXI1GSc/gzKDvV55PMbLtnmupEDfnY9SoRu+QatbWQ6da9sI1lhOcNmRMgiNQBXFzaUFrG+SQ==", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.826.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.826.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.826.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.11", - "@smithy/middleware-retry": "^4.1.12", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.3", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.19", - "@smithy/util-defaults-mode-node": "^4.0.19", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.5", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/token-providers": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.826.0.tgz", - "integrity": "sha512-iCOcVAqGPSHtQL8ZBXifZMEcHyUl9wJ8HvLZ5l1ohA/3ZNP+dqEPGi7jfhR5jZKs+xyp2jxByFqfil9PjI9c5A==", - "dependencies": { - "@aws-sdk/core": "3.826.0", - "@aws-sdk/nested-clients": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.826.0.tgz", - "integrity": "sha512-wHw6bZQWIMcFF/8r03aY9Itp6JLBYY4absGGhCDK1dc3tPEfi8NVSdb05a/Oz+g4TVaDdxLo0OQ/OKMS1DFRHQ==", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, "node_modules/@aws-sdk/client-secrets-manager": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.825.0.tgz", - "integrity": "sha512-cOryB1tID+u9pD2fiyt0wG+o++9UUASS0EdtVND0KW5N4FV/fCKDYmKsH4Lp3fKKEtTVqtD2szQ24Mnx1shYZA==", + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.835.0.tgz", + "integrity": "sha512-w8xIFhxP54kRdmTuRjxOAgNU7MCSgVieXx5pUxMD6B92dpqDTjnVFgTDX8fpUFZSrSwe5dOCiHEDKZsV20YNaQ==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.825.0", - "@aws-sdk/credential-provider-node": "3.825.0", + "@aws-sdk/core": "3.835.0", + "@aws-sdk/credential-provider-node": "3.835.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.825.0", + "@aws-sdk/middleware-user-agent": "3.835.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.825.0", + "@aws-sdk/util-user-agent-node": "3.835.0", "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.2", + "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.10", - "@smithy/middleware-retry": "^4.1.11", + "@smithy/middleware-endpoint": "^4.1.12", + "@smithy/middleware-retry": "^4.1.13", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.2", + "@smithy/smithy-client": "^4.4.4", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.18", - "@smithy/util-defaults-mode-node": "^4.0.18", + "@smithy/util-defaults-mode-browser": "^4.0.20", + "@smithy/util-defaults-mode-node": "^4.0.20", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.5", + "@smithy/util-retry": "^4.0.6", "@smithy/util-utf8": "^4.0.0", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", @@ -542,46 +249,47 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.825.0.tgz", - "integrity": "sha512-U0J2RQUsxiin+uEYR8atMByojuvhtWvvEVSD2MhSUUnCa7BSu/H+4SbREBvnGDJ2nezrYh59bkSQBlp9c3Z9gg==", + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.835.0.tgz", + "integrity": "sha512-4J19IcBKU5vL8yw/YWEvbwEGcmCli0rpRyxG53v0K5/3weVPxVBbKfkWcjWVQ4qdxNz2uInfbTde4BRBFxWllQ==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.825.0", + "@aws-sdk/core": "3.835.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.825.0", + "@aws-sdk/middleware-user-agent": "3.835.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.825.0", + "@aws-sdk/util-user-agent-node": "3.835.0", "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.2", + "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.10", - "@smithy/middleware-retry": "^4.1.11", + "@smithy/middleware-endpoint": "^4.1.12", + "@smithy/middleware-retry": "^4.1.13", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.2", + "@smithy/smithy-client": "^4.4.4", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.18", - "@smithy/util-defaults-mode-node": "^4.0.18", + "@smithy/util-defaults-mode-browser": "^4.0.20", + "@smithy/util-defaults-mode-node": "^4.0.20", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.5", + "@smithy/util-retry": "^4.0.6", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -590,18 +298,19 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.825.0.tgz", - "integrity": "sha512-UsdK6l62skh6mqY/La4xvehNj5sUl/eZ2N+8mNTHZKW4U+tiRESdrw1t/Z3r/NUAu7Tbmp+DHbUu+5K1BBY6YQ==", + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.835.0.tgz", + "integrity": "sha512-7mnf4xbaLI8rkDa+w6fUU48dG6yDuOgLXEPe4Ut3SbMp1ceJBPMozNHbCwkiyHk3HpxZYf8eVy0wXhJMrxZq5w==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.821.0", "@aws-sdk/xml-builder": "3.821.0", - "@smithy/core": "^3.5.2", + "@smithy/core": "^3.5.3", "@smithy/node-config-provider": "^4.1.3", "@smithy/property-provider": "^4.0.4", "@smithy/protocol-http": "^5.1.2", "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.2", + "@smithy/smithy-client": "^4.4.4", "@smithy/types": "^4.3.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", @@ -615,11 +324,12 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.825.0.tgz", - "integrity": "sha512-Ptkbhj4K1un+GIz5fmTLVCFtWv9rcbaCLgdZszudo/ZqLP0QzAoACADGYFFkPGYr2o51COKkgKPhHWl7FNEq6A==", + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.835.0.tgz", + "integrity": "sha512-U9LFWe7+ephNyekpUbzT7o6SmJTmn6xkrPkE0D7pbLojnPVi/8SZKyjtgQGIsAv+2kFkOCqMOIYUKd/0pE7uew==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.825.0", + "@aws-sdk/core": "3.835.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", @@ -630,17 +340,18 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.825.0.tgz", - "integrity": "sha512-r0V0rlNCjnFLfYfUqP6TlwAo+YgWxIkrgUb/K6mV2XCBElbFZlc9oPzMOJCmHF/+D6S60FLlMC9AnFopnEZ3/A==", + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.835.0.tgz", + "integrity": "sha512-jCdNEsQklil7frDm/BuVKl4ubVoQHRbV6fnkOjmxAJz0/v7cR8JP0jBGlqKKzh3ROh5/vo1/5VUZbCTLpc9dSg==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.825.0", + "@aws-sdk/core": "3.835.0", "@aws-sdk/types": "3.821.0", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/node-http-handler": "^4.0.6", "@smithy/property-provider": "^4.0.4", "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.2", + "@smithy/smithy-client": "^4.4.4", "@smithy/types": "^4.3.1", "@smithy/util-stream": "^4.2.2", "tslib": "^2.6.2" @@ -650,17 +361,18 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.825.0.tgz", - "integrity": "sha512-HDYopAiIGTLLhybI8jEuKGWdVUnKkkotwXHwvu8ttL5qgs13A6a/iWiREe71fmYH2fGT2URJE9+xeHa2oxohyQ==", - "dependencies": { - "@aws-sdk/core": "3.825.0", - "@aws-sdk/credential-provider-env": "3.825.0", - "@aws-sdk/credential-provider-http": "3.825.0", - "@aws-sdk/credential-provider-process": "3.825.0", - "@aws-sdk/credential-provider-sso": "3.825.0", - "@aws-sdk/credential-provider-web-identity": "3.825.0", - "@aws-sdk/nested-clients": "3.825.0", + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.835.0.tgz", + "integrity": "sha512-nqF6rYRAnJedmvDfrfKygzyeADcduDvtvn7GlbQQbXKeR2l7KnCdhuxHa0FALLvspkHiBx7NtInmvnd5IMuWsw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.835.0", + "@aws-sdk/credential-provider-env": "3.835.0", + "@aws-sdk/credential-provider-http": "3.835.0", + "@aws-sdk/credential-provider-process": "3.835.0", + "@aws-sdk/credential-provider-sso": "3.835.0", + "@aws-sdk/credential-provider-web-identity": "3.835.0", + "@aws-sdk/nested-clients": "3.835.0", "@aws-sdk/types": "3.821.0", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/property-provider": "^4.0.4", @@ -673,16 +385,17 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.825.0.tgz", - "integrity": "sha512-qWMrrUgWFQN7nkMdQYzWF/Z/fhUctCjwTQQD/qNSs42qt3sxmC00SZcqwPn9N8S9R/hLmu5z6fefVF4o20nGng==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.825.0", - "@aws-sdk/credential-provider-http": "3.825.0", - "@aws-sdk/credential-provider-ini": "3.825.0", - "@aws-sdk/credential-provider-process": "3.825.0", - "@aws-sdk/credential-provider-sso": "3.825.0", - "@aws-sdk/credential-provider-web-identity": "3.825.0", + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.835.0.tgz", + "integrity": "sha512-77B8elyZlaEd7vDYyCnYtVLuagIBwuJ0AQ98/36JMGrYX7TT8UVAhiDAfVe0NdUOMORvDNFfzL06VBm7wittYw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.835.0", + "@aws-sdk/credential-provider-http": "3.835.0", + "@aws-sdk/credential-provider-ini": "3.835.0", + "@aws-sdk/credential-provider-process": "3.835.0", + "@aws-sdk/credential-provider-sso": "3.835.0", + "@aws-sdk/credential-provider-web-identity": "3.835.0", "@aws-sdk/types": "3.821.0", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/property-provider": "^4.0.4", @@ -695,11 +408,12 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.825.0.tgz", - "integrity": "sha512-QQoOBQAXuBfD6BCg61Hl5EkdrLyFSQCNRHVLjAO5WYQGyiPb9iTZPqo9sPwyOnCMpZE1k2EOwQ+FsnZh0xSa3Q==", + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.835.0.tgz", + "integrity": "sha512-qXkTt5pAhSi2Mp9GdgceZZFo/cFYrA735efqi/Re/nf0lpqBp8mRM8xv+iAaPHV4Q10q0DlkbEidT1DhxdT/+w==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.825.0", + "@aws-sdk/core": "3.835.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", @@ -711,13 +425,14 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.825.0.tgz", - "integrity": "sha512-ppwsN8tuwwJKvNnllkrhIx7AQv4r5uiNf5FTIkyeJ+3p67wgJeJye+0SP64IEkdmG7YxCaU2YkdSvyHud+D5og==", - "dependencies": { - "@aws-sdk/client-sso": "3.825.0", - "@aws-sdk/core": "3.825.0", - "@aws-sdk/token-providers": "3.825.0", + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.835.0.tgz", + "integrity": "sha512-jAiEMryaPFXayYGszrc7NcgZA/zrrE3QvvvUBh/Udasg+9Qp5ZELdJCm/p98twNyY9n5i6Ex6VgvdxZ7+iEheQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.835.0", + "@aws-sdk/core": "3.835.0", + "@aws-sdk/token-providers": "3.835.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", @@ -729,12 +444,13 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.825.0.tgz", - "integrity": "sha512-cyL5xHqtvBUpflkmdQSkvjD/t+Dl/ZSXvPnc9KF79xDpuraZ5tFP1l0B6rIEu7dUzUh8XG+7m2CZ6TEs6QU33Q==", + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.835.0.tgz", + "integrity": "sha512-zfleEFXDLlcJ7cyfS4xSyCRpd8SVlYZfH3rp0pg2vPYKbnmXVE0r+gPIYXl4L+Yz4A2tizYl63nKCNdtbxadog==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.825.0", - "@aws-sdk/nested-clients": "3.825.0", + "@aws-sdk/core": "3.835.0", + "@aws-sdk/nested-clients": "3.835.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", @@ -748,6 +464,7 @@ "version": "3.821.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.821.0.tgz", "integrity": "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/protocol-http": "^5.1.2", @@ -762,6 +479,7 @@ "version": "3.821.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.821.0.tgz", "integrity": "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/types": "^4.3.1", @@ -775,6 +493,7 @@ "version": "3.821.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.821.0.tgz", "integrity": "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/protocol-http": "^5.1.2", @@ -786,16 +505,17 @@ } }, "node_modules/@aws-sdk/middleware-sdk-ec2": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-ec2/-/middleware-sdk-ec2-3.826.0.tgz", - "integrity": "sha512-qonwFJddYtVTXEj+GGjqHWqlSYDmKI4ZIf7iozNgucBzP5+zdFuyjvdQIAFLAv/joQphGv4P799PK2Elb5ZzpA==", + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-ec2/-/middleware-sdk-ec2-3.835.0.tgz", + "integrity": "sha512-wepCXGo+rsxpOPxZcInyyesCQJ21nJjX6ldRs6ige/NeaDQlLjOOsbQCXUT/b4xoFFjUxUUG6zZHudK7jFzgIA==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.821.0", "@aws-sdk/util-format-url": "3.821.0", - "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-endpoint": "^4.1.12", "@smithy/protocol-http": "^5.1.2", "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.3", + "@smithy/smithy-client": "^4.4.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, @@ -804,14 +524,15 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.825.0.tgz", - "integrity": "sha512-3ZZOPU3GE5cqKl6VFDwiL8KIvlrrQJ4rgYkeiF+m5kA0eXV2xFOwoLgm3AmPB+6kfo9HQ0N74KKJV0teS5nO6Q==", + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.835.0.tgz", + "integrity": "sha512-2gmAYygeE/gzhyF2XlkcbMLYFTbNfV61n+iCFa/ZofJHXYE+RxSyl5g4kujLEs7bVZHmjQZJXhprVSkGccq3/w==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.825.0", + "@aws-sdk/core": "3.835.0", "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", - "@smithy/core": "^3.5.2", + "@aws-sdk/util-endpoints": "3.828.0", + "@smithy/core": "^3.5.3", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -821,46 +542,47 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.825.0.tgz", - "integrity": "sha512-OuV2pypFAv52Lty8eXWVWyyOywVmMAsgH6Gq3SA06pHEtcE+ghVIW9ByegecyfMRUpedAiovARKNy0pfGX05Pg==", + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.835.0.tgz", + "integrity": "sha512-UtmOO0U5QkicjCEv+B32qqRAnS7o2ZkZhC+i3ccH1h3fsfaBshpuuNBwOYAzRCRBeKW5fw3ANFrV/+2FTp4jWg==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.825.0", + "@aws-sdk/core": "3.835.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.825.0", + "@aws-sdk/middleware-user-agent": "3.835.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.825.0", + "@aws-sdk/util-user-agent-node": "3.835.0", "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.2", + "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.10", - "@smithy/middleware-retry": "^4.1.11", + "@smithy/middleware-endpoint": "^4.1.12", + "@smithy/middleware-retry": "^4.1.13", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.2", + "@smithy/smithy-client": "^4.4.4", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.18", - "@smithy/util-defaults-mode-node": "^4.0.18", + "@smithy/util-defaults-mode-browser": "^4.0.20", + "@smithy/util-defaults-mode-node": "^4.0.20", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.5", + "@smithy/util-retry": "^4.0.6", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -872,6 +594,7 @@ "version": "3.821.0", "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.821.0.tgz", "integrity": "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/node-config-provider": "^4.1.3", @@ -885,12 +608,13 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.825.0.tgz", - "integrity": "sha512-a3HbF6h1Gq2vA+mGlxFe3op65wNK6dBRmp3GFwsPVQ+OFTbZJi86FCljMfBrv+BGYUkp503/IPC49wuRHOdcZA==", + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.835.0.tgz", + "integrity": "sha512-zN1P3BE+Rv7w7q/CDA8VCQox6SE9QTn0vDtQ47AHA3eXZQQgYzBqgoLgJxR9rKKBIRGZqInJa/VRskLL95VliQ==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.825.0", - "@aws-sdk/nested-clients": "3.825.0", + "@aws-sdk/core": "3.835.0", + "@aws-sdk/nested-clients": "3.835.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", @@ -905,6 +629,7 @@ "version": "3.821.0", "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.821.0.tgz", "integrity": "sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -914,9 +639,10 @@ } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.821.0.tgz", - "integrity": "sha512-Uknt/zUZnLE76zaAAPEayOeF5/4IZ2puTFXvcSCWHsi9m3tqbb9UozlnlVqvCZLCRWfQryZQoG2W4XSS3qgk5A==", + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.828.0.tgz", + "integrity": "sha512-RvKch111SblqdkPzg3oCIdlGxlQs+k+P7Etory9FmxPHyPDvsP1j1c74PmgYqtzzMWmoXTjd+c9naUHh9xG8xg==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/types": "^4.3.1", @@ -931,6 +657,7 @@ "version": "3.821.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.821.0.tgz", "integrity": "sha512-h+xqmPToxDrZ0a7rxE1a8Oh4zpWfZe9oiQUphGtfiGFA6j75UiURH5J3MmGHa/G4t15I3iLLbYtUXxvb1i7evg==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/querystring-builder": "^4.0.4", @@ -945,6 +672,7 @@ "version": "3.804.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz", "integrity": "sha512-zVoRfpmBVPodYlnMjgVjfGoEZagyRF5IPn3Uo6ZvOZp24chnW/FRstH7ESDHDDRga4z3V+ElUQHKpFDXWyBW5A==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -956,6 +684,7 @@ "version": "3.821.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.821.0.tgz", "integrity": "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/types": "^4.3.1", @@ -964,11 +693,12 @@ } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.825.0.tgz", - "integrity": "sha512-RfB0w9YJSsFGsbrzOQ1VE2O4NwR6gxelUvmz8PzuerPCg4iD4JW7hSCmnoAEi51Xnq0bNeCsnhzXJzIlPe04jA==", + "version": "3.835.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.835.0.tgz", + "integrity": "sha512-gY63QZ4W5w9JYHYuqvUxiVGpn7IbCt1ODPQB0ZZwGGr3WRmK+yyZxCtFjbYhEQDQLgTWpf8YgVxgQLv2ps0PJg==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.825.0", + "@aws-sdk/middleware-user-agent": "3.835.0", "@aws-sdk/types": "3.821.0", "@smithy/node-config-provider": "^4.1.3", "@smithy/types": "^4.3.1", @@ -990,6 +720,7 @@ "version": "3.821.0", "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz", "integrity": "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -998,48 +729,476 @@ "node": ">=18.0.0" } }, - "node_modules/@smithy/abort-controller": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", - "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", + "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@smithy/config-resolver": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.4.tgz", - "integrity": "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==", - "dependencies": { - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", + "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@smithy/core": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.5.3.tgz", - "integrity": "sha512-xa5byV9fEguZNofCclv6v9ra0FYh5FATQW/da7FQUVTic94DfrN/NvmKZjrMyzbpqfot9ZjBaO8U1UeTbmSLuA==", - "dependencies": { - "@smithy/middleware-serde": "^4.0.8", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-stream": "^4.2.2", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", + "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", + "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", + "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", + "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", + "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", + "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", + "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", + "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", + "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", + "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", + "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", + "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", + "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", + "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", + "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", + "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", + "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", + "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", + "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", + "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", + "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.4.tgz", + "integrity": "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.5.3.tgz", + "integrity": "sha512-xa5byV9fEguZNofCclv6v9ra0FYh5FATQW/da7FQUVTic94DfrN/NvmKZjrMyzbpqfot9ZjBaO8U1UeTbmSLuA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.0.8", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, "engines": { "node": ">=18.0.0" } @@ -1048,6 +1207,7 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.6.tgz", "integrity": "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==", + "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^4.1.3", "@smithy/property-provider": "^4.0.4", @@ -1063,6 +1223,7 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.4.tgz", "integrity": "sha512-AMtBR5pHppYMVD7z7G+OlHHAcgAN7v0kVKEpHuTO4Gb199Gowh0taYi9oDStFeUhetkeP55JLSVlTW1n9rFtUw==", + "license": "Apache-2.0", "dependencies": { "@smithy/protocol-http": "^5.1.2", "@smithy/querystring-builder": "^4.0.4", @@ -1078,6 +1239,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.4.tgz", "integrity": "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.3.1", "@smithy/util-buffer-from": "^4.0.0", @@ -1092,6 +1254,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.4.tgz", "integrity": "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -1104,6 +1267,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -1115,6 +1279,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.4.tgz", "integrity": "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==", + "license": "Apache-2.0", "dependencies": { "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", @@ -1125,9 +1290,10 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.11.tgz", - "integrity": "sha512-zDogwtRLzKl58lVS8wPcARevFZNBOOqnmzWWxVe9XiaXU2CADFjvJ9XfNibgkOWs08sxLuSr81NrpY4mgp9OwQ==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.12.tgz", + "integrity": "sha512-Piy/9UOjh5FtEXhybjPwyOHcC/pGHFknl2Gc/q1YbEkngxY6eQwvBvZTNamXpyDAHCuP3h+lymcVcdyO3WdGqQ==", + "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.5.3", "@smithy/middleware-serde": "^4.0.8", @@ -1143,17 +1309,18 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.12.tgz", - "integrity": "sha512-wvIH70c4e91NtRxdaLZF+mbLZ/HcC6yg7ySKUiufL6ESp6zJUSnJucZ309AvG9nqCFHSRB5I6T3Ez1Q9wCh0Ww==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.13.tgz", + "integrity": "sha512-5ILvPCJevTcGpl7wAvSV9HKbIGS2Wxz505d0b5dP9kmjBhsFm1SAsSLIteMn925hlxPUkOsjcjMyaEiQDr9s4w==", + "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^4.1.3", "@smithy/protocol-http": "^5.1.2", - "@smithy/service-error-classification": "^4.0.5", - "@smithy/smithy-client": "^4.4.3", + "@smithy/service-error-classification": "^4.0.6", + "@smithy/smithy-client": "^4.4.4", "@smithy/types": "^4.3.1", "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.5", + "@smithy/util-retry": "^4.0.6", "tslib": "^2.6.2", "uuid": "^9.0.1" }, @@ -1165,6 +1332,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz", "integrity": "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==", + "license": "Apache-2.0", "dependencies": { "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", @@ -1178,6 +1346,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.4.tgz", "integrity": "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -1190,6 +1359,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.3.tgz", "integrity": "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==", + "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", @@ -1204,6 +1374,7 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", + "license": "Apache-2.0", "dependencies": { "@smithy/abort-controller": "^4.0.4", "@smithy/protocol-http": "^5.1.2", @@ -1219,6 +1390,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.4.tgz", "integrity": "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -1231,6 +1403,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.2.tgz", "integrity": "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -1243,6 +1416,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.4.tgz", "integrity": "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.3.1", "@smithy/util-uri-escape": "^4.0.0", @@ -1256,6 +1430,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.4.tgz", "integrity": "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -1265,9 +1440,10 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.5.tgz", - "integrity": "sha512-LvcfhrnCBvCmTee81pRlh1F39yTS/+kYleVeLCwNtkY8wtGg8V/ca9rbZZvYIl8OjlMtL6KIjaiL/lgVqHD2nA==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.6.tgz", + "integrity": "sha512-RRoTDL//7xi4tn5FrN2NzH17jbgmnKidUqd4KvquT0954/i6CXXkh1884jBiunq24g9cGtPBEXlU40W6EpNOOg==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.3.1" }, @@ -1279,6 +1455,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.4.tgz", "integrity": "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -1291,6 +1468,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.2.tgz", "integrity": "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==", + "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "@smithy/protocol-http": "^5.1.2", @@ -1306,12 +1484,13 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.3.tgz", - "integrity": "sha512-xxzNYgA0HD6ETCe5QJubsxP0hQH3QK3kbpJz3QrosBCuIWyEXLR/CO5hFb2OeawEKUxMNhz3a1nuJNN2np2RMA==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.4.tgz", + "integrity": "sha512-38Ivn1VoArWi+wvJeW6rGl9lcuViYjmGfaZaBgOlFEyoQSIl2Rnr3uOWzwu3FE8NIvHflQVkwbveMQxBAEbd1A==", + "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.5.3", - "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-endpoint": "^4.1.12", "@smithy/middleware-stack": "^4.0.4", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", @@ -1326,6 +1505,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -1337,6 +1517,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.4.tgz", "integrity": "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==", + "license": "Apache-2.0", "dependencies": { "@smithy/querystring-parser": "^4.0.4", "@smithy/types": "^4.3.1", @@ -1350,6 +1531,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", + "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", @@ -1363,6 +1545,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -1374,6 +1557,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -1385,6 +1569,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", + "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" @@ -1397,6 +1582,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -1405,12 +1591,13 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.0.19", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.19.tgz", - "integrity": "sha512-mvLMh87xSmQrV5XqnUYEPoiFFeEGYeAKIDDKdhE2ahqitm8OHM3aSvhqL6rrK6wm1brIk90JhxDf5lf2hbrLbQ==", + "version": "4.0.20", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.20.tgz", + "integrity": "sha512-496BbDMx/8kQrvlhT0EsX7JM7yVpK7CACmG3LsqMX9RaJnF7M/OVlfbxoRceUp5o5S0HqBnV8/xGOX7MYCv2Gw==", + "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.0.4", - "@smithy/smithy-client": "^4.4.3", + "@smithy/smithy-client": "^4.4.4", "@smithy/types": "^4.3.1", "bowser": "^2.11.0", "tslib": "^2.6.2" @@ -1420,15 +1607,16 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.0.19", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.19.tgz", - "integrity": "sha512-8tYnx+LUfj6m+zkUUIrIQJxPM1xVxfRBvoGHua7R/i6qAxOMjqR6CpEpDwKoIs1o0+hOjGvkKE23CafKL0vJ9w==", + "version": "4.0.20", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.20.tgz", + "integrity": "sha512-QsGHToYvRCoMyJQr/bXLG7L+nXNxICpG5LI1lRL0wkdkvLIxP89r4O+LHLWI9UeLzylxJ7VPnsTR/ADJ+F71/w==", + "license": "Apache-2.0", "dependencies": { "@smithy/config-resolver": "^4.1.4", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/node-config-provider": "^4.1.3", "@smithy/property-provider": "^4.0.4", - "@smithy/smithy-client": "^4.4.3", + "@smithy/smithy-client": "^4.4.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, @@ -1440,6 +1628,7 @@ "version": "3.0.6", "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.6.tgz", "integrity": "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==", + "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^4.1.3", "@smithy/types": "^4.3.1", @@ -1453,6 +1642,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -1464,6 +1654,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.4.tgz", "integrity": "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -1473,11 +1664,12 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.5.tgz", - "integrity": "sha512-V7MSjVDTlEt/plmOFBn1762Dyu5uqMrV2Pl2X0dYk4XvWfdWJNe9Bs5Bzb56wkCuiWjSfClVMGcsuKrGj7S/yg==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.6.tgz", + "integrity": "sha512-+YekoF2CaSMv6zKrA6iI/N9yva3Gzn4L6n35Luydweu5MMPYpiGZlWqehPHDHyNbnyaYlz/WJyYAZnC+loBDZg==", + "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.0.5", + "@smithy/service-error-classification": "^4.0.6", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, @@ -1489,6 +1681,7 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.2.tgz", "integrity": "sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w==", + "license": "Apache-2.0", "dependencies": { "@smithy/fetch-http-handler": "^5.0.4", "@smithy/node-http-handler": "^4.0.6", @@ -1507,6 +1700,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -1518,6 +1712,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" @@ -1530,6 +1725,7 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.5.tgz", "integrity": "sha512-4QvC49HTteI1gfemu0I1syWovJgPvGn7CVUoN9ZFkdvr/cCFkrEL7qNCdx/2eICqDWEGnnr68oMdSIPCLAriSQ==", + "license": "Apache-2.0", "dependencies": { "@smithy/abort-controller": "^4.0.4", "@smithy/types": "^4.3.1", @@ -1542,12 +1738,55 @@ "node_modules/@types/uuid": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==" + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "license": "MIT" }, "node_modules/bowser": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", + "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.5", + "@esbuild/android-arm": "0.25.5", + "@esbuild/android-arm64": "0.25.5", + "@esbuild/android-x64": "0.25.5", + "@esbuild/darwin-arm64": "0.25.5", + "@esbuild/darwin-x64": "0.25.5", + "@esbuild/freebsd-arm64": "0.25.5", + "@esbuild/freebsd-x64": "0.25.5", + "@esbuild/linux-arm": "0.25.5", + "@esbuild/linux-arm64": "0.25.5", + "@esbuild/linux-ia32": "0.25.5", + "@esbuild/linux-loong64": "0.25.5", + "@esbuild/linux-mips64el": "0.25.5", + "@esbuild/linux-ppc64": "0.25.5", + "@esbuild/linux-riscv64": "0.25.5", + "@esbuild/linux-s390x": "0.25.5", + "@esbuild/linux-x64": "0.25.5", + "@esbuild/netbsd-arm64": "0.25.5", + "@esbuild/netbsd-x64": "0.25.5", + "@esbuild/openbsd-arm64": "0.25.5", + "@esbuild/openbsd-x64": "0.25.5", + "@esbuild/sunos-x64": "0.25.5", + "@esbuild/win32-arm64": "0.25.5", + "@esbuild/win32-ia32": "0.25.5", + "@esbuild/win32-x64": "0.25.5" + } }, "node_modules/fast-xml-parser": { "version": "4.4.1", @@ -1563,6 +1802,7 @@ "url": "https://paypal.me/naturalintelligence" } ], + "license": "MIT", "dependencies": { "strnum": "^1.0.5" }, @@ -1574,6 +1814,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" } @@ -1587,12 +1828,14 @@ "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" } - ] + ], + "license": "MIT" }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/uuid": { "version": "9.0.1", @@ -1602,1309 +1845,10 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } } - }, - "dependencies": { - "@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "requires": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "requires": { - "tslib": "^2.6.2" - } - }, - "@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "requires": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - } - }, - "@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "requires": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - } - } - } - }, - "@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "requires": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - } - }, - "@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "requires": { - "tslib": "^2.6.2" - } - }, - "@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "requires": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - }, - "dependencies": { - "@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "requires": { - "tslib": "^2.6.2" - } - }, - "@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "requires": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - } - }, - "@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "requires": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - } - } - } - }, - "@aws-sdk/client-ec2": { - "version": "3.827.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-ec2/-/client-ec2-3.827.0.tgz", - "integrity": "sha512-vOALW4vUcsk9+dlNIgyyN5msKpy7TN+TxkFeHwVQOXPzkL5/0V+x1UWxxmUKnhOXxfG2JK3KD9+A9S9IkMHCKQ==", - "requires": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.826.0", - "@aws-sdk/credential-provider-node": "3.826.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-sdk-ec2": "3.826.0", - "@aws-sdk/middleware-user-agent": "3.826.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.826.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.11", - "@smithy/middleware-retry": "^4.1.12", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.3", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.19", - "@smithy/util-defaults-mode-node": "^4.0.19", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.5", - "@smithy/util-utf8": "^4.0.0", - "@smithy/util-waiter": "^4.0.5", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "dependencies": { - "@aws-sdk/client-sso": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.826.0.tgz", - "integrity": "sha512-/FEKnUC3xPkLL4RuRydwzx+y4b55HIX6qLPbGnyIs+sNmCUyc/62ijtV1Ml+b++YzEF6jWNBsJOxeyZdgrJ3Ig==", - "requires": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.826.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.826.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.826.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.11", - "@smithy/middleware-retry": "^4.1.12", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.3", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.19", - "@smithy/util-defaults-mode-node": "^4.0.19", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.5", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/core": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.826.0.tgz", - "integrity": "sha512-BGbQYzWj3ps+dblq33FY5tz/SsgJCcXX0zjQlSC07tYvU1jHTUvsefphyig+fY38xZ4wdKjbTop+KUmXUYrOXw==", - "requires": { - "@aws-sdk/types": "3.821.0", - "@aws-sdk/xml-builder": "3.821.0", - "@smithy/core": "^3.5.3", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.3", - "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-utf8": "^4.0.0", - "fast-xml-parser": "4.4.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/credential-provider-env": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.826.0.tgz", - "integrity": "sha512-DK3pQY8+iKK3MGDdC3uOZQ2psU01obaKlTYhEwNu4VWzgwQL4Vi3sWj4xSWGEK41vqZxiRLq6fOq7ysRI+qEZA==", - "requires": { - "@aws-sdk/core": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/credential-provider-http": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.826.0.tgz", - "integrity": "sha512-N+IVZBh+yx/9GbMZTKO/gErBi/FYZQtcFRItoLbY+6WU+0cSWyZYfkoeOxHmQV3iX9k65oljERIWUmL9x6OSQg==", - "requires": { - "@aws-sdk/core": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.3", - "@smithy/types": "^4.3.1", - "@smithy/util-stream": "^4.2.2", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/credential-provider-ini": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.826.0.tgz", - "integrity": "sha512-g7n+qSklq/Lzjxe2Ke5QFNCgYn26a3ydZnbFIk8QqYin4pzG+qiunaqJjpV3c/EeHMlfK8bBc7MXAylKzGRccQ==", - "requires": { - "@aws-sdk/core": "3.826.0", - "@aws-sdk/credential-provider-env": "3.826.0", - "@aws-sdk/credential-provider-http": "3.826.0", - "@aws-sdk/credential-provider-process": "3.826.0", - "@aws-sdk/credential-provider-sso": "3.826.0", - "@aws-sdk/credential-provider-web-identity": "3.826.0", - "@aws-sdk/nested-clients": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/credential-provider-node": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.826.0.tgz", - "integrity": "sha512-UfIJXxHjmSxH6bea00HBPLkjNI2D04enQA/xNLZvB+4xtzt1/gYdCis1P4/73f5aGVVVB4/zQMobBbnjkrmbQw==", - "requires": { - "@aws-sdk/credential-provider-env": "3.826.0", - "@aws-sdk/credential-provider-http": "3.826.0", - "@aws-sdk/credential-provider-ini": "3.826.0", - "@aws-sdk/credential-provider-process": "3.826.0", - "@aws-sdk/credential-provider-sso": "3.826.0", - "@aws-sdk/credential-provider-web-identity": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/credential-provider-process": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.826.0.tgz", - "integrity": "sha512-kURrc4amu3NLtw1yZw7EoLNEVhmOMRUTs+chaNcmS+ERm3yK0nKjaJzmKahmwlTQTSl3wJ8jjK7x962VPo+zWw==", - "requires": { - "@aws-sdk/core": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/credential-provider-sso": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.826.0.tgz", - "integrity": "sha512-F19J3zcfoom6OnQ0MyAtvduVKQXPgkz9i5ExSO01J2CzjbyMhCDA99qAjHYe+LwhW+W7P/jzBPd0+uOQ2Nhh9Q==", - "requires": { - "@aws-sdk/client-sso": "3.826.0", - "@aws-sdk/core": "3.826.0", - "@aws-sdk/token-providers": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/credential-provider-web-identity": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.826.0.tgz", - "integrity": "sha512-o27GZ6Hy7qhuvMFVUL2eFEpBzf33Jaa/x3u3SHwU0nL7ko7jmbpeF0x4+wmagpI9X2IvVlUxIs0VaQ3YayPLEA==", - "requires": { - "@aws-sdk/core": "3.826.0", - "@aws-sdk/nested-clients": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/middleware-user-agent": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.826.0.tgz", - "integrity": "sha512-j404+EcfBbtTlAhyObjXbdKwwDXO1pCxHvR5Fw8FXNvp/H330j6YnXgs3SJ6d3bZUwUJ/ztPx2S5AlBbLVLDFw==", - "requires": { - "@aws-sdk/core": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", - "@smithy/core": "^3.5.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/nested-clients": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.826.0.tgz", - "integrity": "sha512-p7olPq0uTtHqGuXI1GSc/gzKDvV55PMbLtnmupEDfnY9SoRu+QatbWQ6da9sI1lhOcNmRMgiNQBXFzaUFrG+SQ==", - "requires": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.826.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.826.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.826.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.11", - "@smithy/middleware-retry": "^4.1.12", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.3", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.19", - "@smithy/util-defaults-mode-node": "^4.0.19", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.5", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/token-providers": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.826.0.tgz", - "integrity": "sha512-iCOcVAqGPSHtQL8ZBXifZMEcHyUl9wJ8HvLZ5l1ohA/3ZNP+dqEPGi7jfhR5jZKs+xyp2jxByFqfil9PjI9c5A==", - "requires": { - "@aws-sdk/core": "3.826.0", - "@aws-sdk/nested-clients": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/util-user-agent-node": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.826.0.tgz", - "integrity": "sha512-wHw6bZQWIMcFF/8r03aY9Itp6JLBYY4absGGhCDK1dc3tPEfi8NVSdb05a/Oz+g4TVaDdxLo0OQ/OKMS1DFRHQ==", - "requires": { - "@aws-sdk/middleware-user-agent": "3.826.0", - "@aws-sdk/types": "3.821.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - } - } - }, - "@aws-sdk/client-secrets-manager": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.825.0.tgz", - "integrity": "sha512-cOryB1tID+u9pD2fiyt0wG+o++9UUASS0EdtVND0KW5N4FV/fCKDYmKsH4Lp3fKKEtTVqtD2szQ24Mnx1shYZA==", - "requires": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.825.0", - "@aws-sdk/credential-provider-node": "3.825.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.825.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.825.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.2", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.10", - "@smithy/middleware-retry": "^4.1.11", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.2", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.18", - "@smithy/util-defaults-mode-node": "^4.0.18", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.5", - "@smithy/util-utf8": "^4.0.0", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - } - }, - "@aws-sdk/client-sso": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.825.0.tgz", - "integrity": "sha512-U0J2RQUsxiin+uEYR8atMByojuvhtWvvEVSD2MhSUUnCa7BSu/H+4SbREBvnGDJ2nezrYh59bkSQBlp9c3Z9gg==", - "requires": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.825.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.825.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.825.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.2", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.10", - "@smithy/middleware-retry": "^4.1.11", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.2", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.18", - "@smithy/util-defaults-mode-node": "^4.0.18", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.5", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/core": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.825.0.tgz", - "integrity": "sha512-UsdK6l62skh6mqY/La4xvehNj5sUl/eZ2N+8mNTHZKW4U+tiRESdrw1t/Z3r/NUAu7Tbmp+DHbUu+5K1BBY6YQ==", - "requires": { - "@aws-sdk/types": "3.821.0", - "@aws-sdk/xml-builder": "3.821.0", - "@smithy/core": "^3.5.2", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.2", - "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-utf8": "^4.0.0", - "fast-xml-parser": "4.4.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/credential-provider-env": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.825.0.tgz", - "integrity": "sha512-Ptkbhj4K1un+GIz5fmTLVCFtWv9rcbaCLgdZszudo/ZqLP0QzAoACADGYFFkPGYr2o51COKkgKPhHWl7FNEq6A==", - "requires": { - "@aws-sdk/core": "3.825.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/credential-provider-http": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.825.0.tgz", - "integrity": "sha512-r0V0rlNCjnFLfYfUqP6TlwAo+YgWxIkrgUb/K6mV2XCBElbFZlc9oPzMOJCmHF/+D6S60FLlMC9AnFopnEZ3/A==", - "requires": { - "@aws-sdk/core": "3.825.0", - "@aws-sdk/types": "3.821.0", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.2", - "@smithy/types": "^4.3.1", - "@smithy/util-stream": "^4.2.2", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/credential-provider-ini": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.825.0.tgz", - "integrity": "sha512-HDYopAiIGTLLhybI8jEuKGWdVUnKkkotwXHwvu8ttL5qgs13A6a/iWiREe71fmYH2fGT2URJE9+xeHa2oxohyQ==", - "requires": { - "@aws-sdk/core": "3.825.0", - "@aws-sdk/credential-provider-env": "3.825.0", - "@aws-sdk/credential-provider-http": "3.825.0", - "@aws-sdk/credential-provider-process": "3.825.0", - "@aws-sdk/credential-provider-sso": "3.825.0", - "@aws-sdk/credential-provider-web-identity": "3.825.0", - "@aws-sdk/nested-clients": "3.825.0", - "@aws-sdk/types": "3.821.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/credential-provider-node": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.825.0.tgz", - "integrity": "sha512-qWMrrUgWFQN7nkMdQYzWF/Z/fhUctCjwTQQD/qNSs42qt3sxmC00SZcqwPn9N8S9R/hLmu5z6fefVF4o20nGng==", - "requires": { - "@aws-sdk/credential-provider-env": "3.825.0", - "@aws-sdk/credential-provider-http": "3.825.0", - "@aws-sdk/credential-provider-ini": "3.825.0", - "@aws-sdk/credential-provider-process": "3.825.0", - "@aws-sdk/credential-provider-sso": "3.825.0", - "@aws-sdk/credential-provider-web-identity": "3.825.0", - "@aws-sdk/types": "3.821.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/credential-provider-process": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.825.0.tgz", - "integrity": "sha512-QQoOBQAXuBfD6BCg61Hl5EkdrLyFSQCNRHVLjAO5WYQGyiPb9iTZPqo9sPwyOnCMpZE1k2EOwQ+FsnZh0xSa3Q==", - "requires": { - "@aws-sdk/core": "3.825.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/credential-provider-sso": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.825.0.tgz", - "integrity": "sha512-ppwsN8tuwwJKvNnllkrhIx7AQv4r5uiNf5FTIkyeJ+3p67wgJeJye+0SP64IEkdmG7YxCaU2YkdSvyHud+D5og==", - "requires": { - "@aws-sdk/client-sso": "3.825.0", - "@aws-sdk/core": "3.825.0", - "@aws-sdk/token-providers": "3.825.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/credential-provider-web-identity": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.825.0.tgz", - "integrity": "sha512-cyL5xHqtvBUpflkmdQSkvjD/t+Dl/ZSXvPnc9KF79xDpuraZ5tFP1l0B6rIEu7dUzUh8XG+7m2CZ6TEs6QU33Q==", - "requires": { - "@aws-sdk/core": "3.825.0", - "@aws-sdk/nested-clients": "3.825.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/middleware-host-header": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.821.0.tgz", - "integrity": "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==", - "requires": { - "@aws-sdk/types": "3.821.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/middleware-logger": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.821.0.tgz", - "integrity": "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==", - "requires": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/middleware-recursion-detection": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.821.0.tgz", - "integrity": "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==", - "requires": { - "@aws-sdk/types": "3.821.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/middleware-sdk-ec2": { - "version": "3.826.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-ec2/-/middleware-sdk-ec2-3.826.0.tgz", - "integrity": "sha512-qonwFJddYtVTXEj+GGjqHWqlSYDmKI4ZIf7iozNgucBzP5+zdFuyjvdQIAFLAv/joQphGv4P799PK2Elb5ZzpA==", - "requires": { - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-format-url": "3.821.0", - "@smithy/middleware-endpoint": "^4.1.11", - "@smithy/protocol-http": "^5.1.2", - "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.3", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/middleware-user-agent": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.825.0.tgz", - "integrity": "sha512-3ZZOPU3GE5cqKl6VFDwiL8KIvlrrQJ4rgYkeiF+m5kA0eXV2xFOwoLgm3AmPB+6kfo9HQ0N74KKJV0teS5nO6Q==", - "requires": { - "@aws-sdk/core": "3.825.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", - "@smithy/core": "^3.5.2", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/nested-clients": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.825.0.tgz", - "integrity": "sha512-OuV2pypFAv52Lty8eXWVWyyOywVmMAsgH6Gq3SA06pHEtcE+ghVIW9ByegecyfMRUpedAiovARKNy0pfGX05Pg==", - "requires": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.825.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.825.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.825.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.2", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.10", - "@smithy/middleware-retry": "^4.1.11", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.2", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.18", - "@smithy/util-defaults-mode-node": "^4.0.18", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.5", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/region-config-resolver": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.821.0.tgz", - "integrity": "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==", - "requires": { - "@aws-sdk/types": "3.821.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/token-providers": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.825.0.tgz", - "integrity": "sha512-a3HbF6h1Gq2vA+mGlxFe3op65wNK6dBRmp3GFwsPVQ+OFTbZJi86FCljMfBrv+BGYUkp503/IPC49wuRHOdcZA==", - "requires": { - "@aws-sdk/core": "3.825.0", - "@aws-sdk/nested-clients": "3.825.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/types": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.821.0.tgz", - "integrity": "sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA==", - "requires": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/util-endpoints": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.821.0.tgz", - "integrity": "sha512-Uknt/zUZnLE76zaAAPEayOeF5/4IZ2puTFXvcSCWHsi9m3tqbb9UozlnlVqvCZLCRWfQryZQoG2W4XSS3qgk5A==", - "requires": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", - "@smithy/util-endpoints": "^3.0.6", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/util-format-url": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.821.0.tgz", - "integrity": "sha512-h+xqmPToxDrZ0a7rxE1a8Oh4zpWfZe9oiQUphGtfiGFA6j75UiURH5J3MmGHa/G4t15I3iLLbYtUXxvb1i7evg==", - "requires": { - "@aws-sdk/types": "3.821.0", - "@smithy/querystring-builder": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/util-locate-window": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz", - "integrity": "sha512-zVoRfpmBVPodYlnMjgVjfGoEZagyRF5IPn3Uo6ZvOZp24chnW/FRstH7ESDHDDRga4z3V+ElUQHKpFDXWyBW5A==", - "requires": { - "tslib": "^2.6.2" - } - }, - "@aws-sdk/util-user-agent-browser": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.821.0.tgz", - "integrity": "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==", - "requires": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/util-user-agent-node": { - "version": "3.825.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.825.0.tgz", - "integrity": "sha512-RfB0w9YJSsFGsbrzOQ1VE2O4NwR6gxelUvmz8PzuerPCg4iD4JW7hSCmnoAEi51Xnq0bNeCsnhzXJzIlPe04jA==", - "requires": { - "@aws-sdk/middleware-user-agent": "3.825.0", - "@aws-sdk/types": "3.821.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/xml-builder": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz", - "integrity": "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==", - "requires": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/abort-controller": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", - "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", - "requires": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/config-resolver": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.4.tgz", - "integrity": "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==", - "requires": { - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "tslib": "^2.6.2" - } - }, - "@smithy/core": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.5.3.tgz", - "integrity": "sha512-xa5byV9fEguZNofCclv6v9ra0FYh5FATQW/da7FQUVTic94DfrN/NvmKZjrMyzbpqfot9ZjBaO8U1UeTbmSLuA==", - "requires": { - "@smithy/middleware-serde": "^4.0.8", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-stream": "^4.2.2", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - } - }, - "@smithy/credential-provider-imds": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.6.tgz", - "integrity": "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==", - "requires": { - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "tslib": "^2.6.2" - } - }, - "@smithy/fetch-http-handler": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.4.tgz", - "integrity": "sha512-AMtBR5pHppYMVD7z7G+OlHHAcgAN7v0kVKEpHuTO4Gb199Gowh0taYi9oDStFeUhetkeP55JLSVlTW1n9rFtUw==", - "requires": { - "@smithy/protocol-http": "^5.1.2", - "@smithy/querystring-builder": "^4.0.4", - "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "tslib": "^2.6.2" - } - }, - "@smithy/hash-node": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.4.tgz", - "integrity": "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==", - "requires": { - "@smithy/types": "^4.3.1", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - } - }, - "@smithy/invalid-dependency": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.4.tgz", - "integrity": "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==", - "requires": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/is-array-buffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", - "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", - "requires": { - "tslib": "^2.6.2" - } - }, - "@smithy/middleware-content-length": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.4.tgz", - "integrity": "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==", - "requires": { - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/middleware-endpoint": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.11.tgz", - "integrity": "sha512-zDogwtRLzKl58lVS8wPcARevFZNBOOqnmzWWxVe9XiaXU2CADFjvJ9XfNibgkOWs08sxLuSr81NrpY4mgp9OwQ==", - "requires": { - "@smithy/core": "^3.5.3", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-middleware": "^4.0.4", - "tslib": "^2.6.2" - } - }, - "@smithy/middleware-retry": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.12.tgz", - "integrity": "sha512-wvIH70c4e91NtRxdaLZF+mbLZ/HcC6yg7ySKUiufL6ESp6zJUSnJucZ309AvG9nqCFHSRB5I6T3Ez1Q9wCh0Ww==", - "requires": { - "@smithy/node-config-provider": "^4.1.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/service-error-classification": "^4.0.5", - "@smithy/smithy-client": "^4.4.3", - "@smithy/types": "^4.3.1", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.5", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - } - }, - "@smithy/middleware-serde": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz", - "integrity": "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==", - "requires": { - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/middleware-stack": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.4.tgz", - "integrity": "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==", - "requires": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/node-config-provider": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.3.tgz", - "integrity": "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==", - "requires": { - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/node-http-handler": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", - "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", - "requires": { - "@smithy/abort-controller": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/querystring-builder": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/property-provider": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.4.tgz", - "integrity": "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==", - "requires": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/protocol-http": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.2.tgz", - "integrity": "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==", - "requires": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/querystring-builder": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.4.tgz", - "integrity": "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==", - "requires": { - "@smithy/types": "^4.3.1", - "@smithy/util-uri-escape": "^4.0.0", - "tslib": "^2.6.2" - } - }, - "@smithy/querystring-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.4.tgz", - "integrity": "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==", - "requires": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/service-error-classification": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.5.tgz", - "integrity": "sha512-LvcfhrnCBvCmTee81pRlh1F39yTS/+kYleVeLCwNtkY8wtGg8V/ca9rbZZvYIl8OjlMtL6KIjaiL/lgVqHD2nA==", - "requires": { - "@smithy/types": "^4.3.1" - } - }, - "@smithy/shared-ini-file-loader": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.4.tgz", - "integrity": "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==", - "requires": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/signature-v4": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.2.tgz", - "integrity": "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==", - "requires": { - "@smithy/is-array-buffer": "^4.0.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-uri-escape": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - } - }, - "@smithy/smithy-client": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.3.tgz", - "integrity": "sha512-xxzNYgA0HD6ETCe5QJubsxP0hQH3QK3kbpJz3QrosBCuIWyEXLR/CO5hFb2OeawEKUxMNhz3a1nuJNN2np2RMA==", - "requires": { - "@smithy/core": "^3.5.3", - "@smithy/middleware-endpoint": "^4.1.11", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "@smithy/util-stream": "^4.2.2", - "tslib": "^2.6.2" - } - }, - "@smithy/types": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", - "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", - "requires": { - "tslib": "^2.6.2" - } - }, - "@smithy/url-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.4.tgz", - "integrity": "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==", - "requires": { - "@smithy/querystring-parser": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/util-base64": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", - "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", - "requires": { - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - } - }, - "@smithy/util-body-length-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", - "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", - "requires": { - "tslib": "^2.6.2" - } - }, - "@smithy/util-body-length-node": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", - "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", - "requires": { - "tslib": "^2.6.2" - } - }, - "@smithy/util-buffer-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", - "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", - "requires": { - "@smithy/is-array-buffer": "^4.0.0", - "tslib": "^2.6.2" - } - }, - "@smithy/util-config-provider": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", - "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", - "requires": { - "tslib": "^2.6.2" - } - }, - "@smithy/util-defaults-mode-browser": { - "version": "4.0.19", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.19.tgz", - "integrity": "sha512-mvLMh87xSmQrV5XqnUYEPoiFFeEGYeAKIDDKdhE2ahqitm8OHM3aSvhqL6rrK6wm1brIk90JhxDf5lf2hbrLbQ==", - "requires": { - "@smithy/property-provider": "^4.0.4", - "@smithy/smithy-client": "^4.4.3", - "@smithy/types": "^4.3.1", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "@smithy/util-defaults-mode-node": { - "version": "4.0.19", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.19.tgz", - "integrity": "sha512-8tYnx+LUfj6m+zkUUIrIQJxPM1xVxfRBvoGHua7R/i6qAxOMjqR6CpEpDwKoIs1o0+hOjGvkKE23CafKL0vJ9w==", - "requires": { - "@smithy/config-resolver": "^4.1.4", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/smithy-client": "^4.4.3", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/util-endpoints": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.6.tgz", - "integrity": "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==", - "requires": { - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/util-hex-encoding": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", - "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", - "requires": { - "tslib": "^2.6.2" - } - }, - "@smithy/util-middleware": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.4.tgz", - "integrity": "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==", - "requires": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/util-retry": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.5.tgz", - "integrity": "sha512-V7MSjVDTlEt/plmOFBn1762Dyu5uqMrV2Pl2X0dYk4XvWfdWJNe9Bs5Bzb56wkCuiWjSfClVMGcsuKrGj7S/yg==", - "requires": { - "@smithy/service-error-classification": "^4.0.5", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@smithy/util-stream": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.2.tgz", - "integrity": "sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w==", - "requires": { - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - } - }, - "@smithy/util-uri-escape": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", - "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", - "requires": { - "tslib": "^2.6.2" - } - }, - "@smithy/util-utf8": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", - "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", - "requires": { - "@smithy/util-buffer-from": "^4.0.0", - "tslib": "^2.6.2" - } - }, - "@smithy/util-waiter": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.5.tgz", - "integrity": "sha512-4QvC49HTteI1gfemu0I1syWovJgPvGn7CVUoN9ZFkdvr/cCFkrEL7qNCdx/2eICqDWEGnnr68oMdSIPCLAriSQ==", - "requires": { - "@smithy/abort-controller": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - } - }, - "@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==" - }, - "bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" - }, - "fast-xml-parser": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", - "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", - "requires": { - "strnum": "^1.0.5" - } - }, - "node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" - }, - "strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==" - }, - "tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, - "uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" - } } } diff --git a/private-ca/server/package.json b/private-ca/server/package.json index 63f47ab..04b064f 100644 --- a/private-ca/server/package.json +++ b/private-ca/server/package.json @@ -2,11 +2,12 @@ "name": "private-ca", "version": "0.0.1", "description": "", - "main": "index.js", + "main": "index_lambda.js", "type": "module", "scripts": { - "start": "node index.js", - "test": "echo \"Error: no test specified\" && exit 1" + "start": "node index_lambda.js", + "test": "echo \"Error: no test specified\" && exit 1", + "build": "node build.mjs" }, "author": "", "license": "ISC", @@ -14,5 +15,8 @@ "@aws-sdk/client-ec2": "^3.827.0", "@aws-sdk/client-secrets-manager": "^3.825.0", "node-forge": "^1.3.1" + }, + "devDependencies": { + "esbuild": "^0.25.5" } } diff --git a/private-ca/update-server-on-lambda.sh b/private-ca/update-server-on-lambda.sh index b5fb6a7..bfb2170 100755 --- a/private-ca/update-server-on-lambda.sh +++ b/private-ca/update-server-on-lambda.sh @@ -4,7 +4,8 @@ PROFILE=${3:-'default'} cd server npm i -zip -qr ./lambda.zip . +npm run build +cd dist && zip -qr ../lambda.zip . && cd .. mv lambda.zip ../ cd .. From 5c80d21d865b18a8ad9812c635785cbc0b8e6ec9 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 25 Jun 2025 10:49:37 +0530 Subject: [PATCH 084/146] fix: add error handling for invalid ARN format --- private-ca/server/generate-client-ssh-cert.js | 6 +++++- private-ca/server/generate-host-ssh-cert.js | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/private-ca/server/generate-client-ssh-cert.js b/private-ca/server/generate-client-ssh-cert.js index 06f5d0d..7cae43c 100644 --- a/private-ca/server/generate-client-ssh-cert.js +++ b/private-ca/server/generate-client-ssh-cert.js @@ -12,7 +12,11 @@ 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 match = arn.match(/\/([^/]+)$/); + if (!match) { + throw new Error(`Invalid ARN format: ${arn}`); + } + const roleName = match[1]; const user_ca = Buffer.from(secret.user_ca, 'base64').toString('utf-8'); certPubkey = Buffer.from(certPubkey, 'base64').toString('utf-8'); diff --git a/private-ca/server/generate-host-ssh-cert.js b/private-ca/server/generate-host-ssh-cert.js index e38c0e1..cf4feed 100644 --- a/private-ca/server/generate-host-ssh-cert.js +++ b/private-ca/server/generate-host-ssh-cert.js @@ -8,7 +8,11 @@ const exec = util.promisify(child_process.exec); export const signHostSSHCertificate = async (callerIdentity, secret, certPubkey, awsEC2Region) => { const arn = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn; - const instanceId = arn.match(/\/([^/]+)$/)?.[1]; + const match = arn.match(/\/([^/]+)$/); + if (!match) { + throw new Error(`Invalid ARN format: ${arn}`); + } + const instanceId = match[1]; const publicIp = await getPublicIpAddress(awsEC2Region, instanceId); From 0c44b2125cbe5b7614e9c164932a7368353cc7e4 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 25 Jun 2025 11:16:14 +0530 Subject: [PATCH 085/146] fix: improve AWS credentials check for adding profile arg in lambda invoke command --- private-ca/client/generate-certificate-aws-cli.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index a27fc91..6c6d86c 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -192,8 +192,8 @@ echo "$json_body" > event.json # On EC2 instances with IAM roles, credentials are fetched from instance metadata, # and using --profile will cause an error if ~/.aws/credentials doesn't exist. AWS_PROFILE_ARG="" -if [[ -n "$AWS_PROFILE" && -f ~/.aws/credentials ]]; then - AWS_PROFILE_ARG="--profile $AWS_PROFILE" +if [[ -n "$AWS_PROFILE" ]] && grep -q "$AWS_PROFILE" ~/.aws/{credentials,config} 2>/dev/null; then + AWS_PROFILE_ARG="--profile $AWS_PROFILE" fi aws lambda invoke \ From e22217121c644d122de3ed7a1f700abaaa5ca1bf Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 25 Jun 2025 11:20:27 +0530 Subject: [PATCH 086/146] fix: update AWS credentials check to use USER_AWS_DIR var for profile argument --- private-ca/client/generate-certificate-aws-cli.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index 6c6d86c..2dc63d1 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -192,7 +192,7 @@ echo "$json_body" > event.json # On EC2 instances with IAM roles, credentials are fetched from instance metadata, # and using --profile will cause an error if ~/.aws/credentials doesn't exist. AWS_PROFILE_ARG="" -if [[ -n "$AWS_PROFILE" ]] && grep -q "$AWS_PROFILE" ~/.aws/{credentials,config} 2>/dev/null; then +if [[ -n "$AWS_PROFILE" ]] && grep -q "$AWS_PROFILE" ${USER_AWS_DIR}/{credentials,config} 2>/dev/null; then AWS_PROFILE_ARG="--profile $AWS_PROFILE" fi From b8054a69ce8ecd35f3b81ad53b66232aa7106354 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 25 Jun 2025 12:14:08 +0530 Subject: [PATCH 087/146] fix: remove output suppression from Lambda update command to show errors --- private-ca/update-server-on-lambda.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/private-ca/update-server-on-lambda.sh b/private-ca/update-server-on-lambda.sh index bfb2170..2dcabcb 100755 --- a/private-ca/update-server-on-lambda.sh +++ b/private-ca/update-server-on-lambda.sh @@ -10,7 +10,9 @@ mv lambda.zip ../ cd .. aws lambda update-function-code \ - --function-name $FUNCTION_NAME \ - --zip-file fileb://lambda.zip --region $REGION --profile $PROFILE >/dev/null 2>&1 + --function-name "$FUNCTION_NAME" \ + --zip-file fileb://lambda.zip \ + --region "$REGION" \ + --profile "$PROFILE" rm -r lambda.zip \ No newline at end of file From 03f50f7db601d7d3e97307790ba7e8e31a39f61e Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 25 Jun 2025 13:56:50 +0530 Subject: [PATCH 088/146] feat: derive secret ID from account ID, return error if secret not found for requestor's account ID --- private-ca/server/index_lambda.js | 9 ++++++++- private-ca/server/package-lock.json | 6 +++--- private-ca/server/secret-manager-utils.js | 15 ++++++++++----- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/private-ca/server/index_lambda.js b/private-ca/server/index_lambda.js index 456f3b8..ba9972d 100644 --- a/private-ca/server/index_lambda.js +++ b/private-ca/server/index_lambda.js @@ -18,9 +18,16 @@ export const handler = async (event) => { // auth const callerIdentity = await getCallerIdentity(event); + const accountId = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Account; // secret - const secret = await getSecret(AWS_SECRETS_REGION, 'privateCA'); + const secret = await getSecret(AWS_SECRETS_REGION, 'privateCA', accountId); + if (!secret) { + return { + statusCode: 401, + body: JSON.stringify({ error: 'This AWS account is not configured to use this service'}), + }; + } // action switch (event.action) { diff --git a/private-ca/server/package-lock.json b/private-ca/server/package-lock.json index b06d133..73ab659 100644 --- a/private-ca/server/package-lock.json +++ b/private-ca/server/package-lock.json @@ -143,9 +143,9 @@ } }, "node_modules/@aws-sdk/client-ec2": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-ec2/-/client-ec2-3.835.0.tgz", - "integrity": "sha512-dqJQ1ju8FBB/v26S5UjirvHbvdOE2uA4x/AVyUa9KClYzKGE3/5Jiz7RtKlBT+1eTJEV9oU6/jYCI45M8Waaug==", + "version": "3.836.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-ec2/-/client-ec2-3.836.0.tgz", + "integrity": "sha512-G+P6689zpW8scOC02uG7SeLyx8aHywa9EJgVEmzc0PGmjVveUit8VjBETF7L0Bt3ejk2voWWdyBFTDDKHf8flQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", diff --git a/private-ca/server/secret-manager-utils.js b/private-ca/server/secret-manager-utils.js index a2e4804..680d8b2 100644 --- a/private-ca/server/secret-manager-utils.js +++ b/private-ca/server/secret-manager-utils.js @@ -1,9 +1,14 @@ import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager"; -export const getSecret = async (secretRegion, secretId) => { +export const getSecret = async (secretRegion, secretPrefix, accountId) => { const client = new SecretsManagerClient({ region: secretRegion }); - const command = new GetSecretValueCommand({ SecretId: secretId }); - const response = await client.send(command); - const secret = JSON.parse(response.SecretString); - return secret; + try { + const command = new GetSecretValueCommand({ SecretId: `${secretPrefix}_${accountId}_secret` }); + const response = await client.send(command); + const secret = JSON.parse(response.SecretString); + return secret; + } catch (err) { + console.log(err); + return null; + } } \ No newline at end of file From cbffd0dea656181e5f22a638c7fa57052287d955 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 25 Jun 2025 14:34:04 +0530 Subject: [PATCH 089/146] fix: show error body and HTTP status code on Lambda request failure --- .../client/generate-certificate-curl.sh | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 63eb954..5241b78 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -156,10 +156,20 @@ EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\" if [[ $CA_ACTION = "generateClientSSHCert" ]]; then - LAMBDA_RESPONSE=$(curl -sf "${CA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON") || { - echo "Failed to contact Lambda CA URL. Ensure the URL is correct and the server is running."; + read -r STATUS_CODE LAMBDA_RESPONSE < <( + curl -s "${CA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" -w "%{http_code}\n" | + { + response=$(cat) + status_code=${response: -3} + body=${response:0:$((${#response}-3))} + echo "$status_code $body" + } + ) + + if [[ "$STATUS_CODE" != "200" ]]; then + echo "CA request failed (Status: ${STATUS_CODE}): ${LAMBDA_RESPONSE}" exit 1; - } + fi ENCODED_CERTIFICATE=$(echo "$LAMBDA_RESPONSE" | jq -er ".certificate") || { echo "Certificate not found in Lambda response. Aborting."; exit 1; @@ -182,10 +192,20 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then # sudo access is required to generate host certificate elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then - LAMBDA_RESPONSE=$(curl -sf "${CA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON") || { - echo "Failed to contact Lambda CA URL. Ensure the URL is correct and the server is running."; + read -r STATUS_CODE LAMBDA_RESPONSE < <( + curl -s "${CA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" -w "%{http_code}\n" | + { + response=$(cat) + status_code=${response: -3} + body=${response:0:$((${#response}-3))} + echo "$status_code $body" + } + ) + + if [[ "$STATUS_CODE" != "200" ]]; then + echo "CA request failed (Status: ${STATUS_CODE}): ${LAMBDA_RESPONSE}" exit 1; - } + fi ENCODED_CERTIFICATE=$(echo "$LAMBDA_RESPONSE" | jq -er ".certificate") || { echo "Certificate not found in Lambda response. Aborting."; exit 1; From f3a055d1c16be8d247908350566b2040eeea6e0c Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 25 Jun 2025 15:18:43 +0530 Subject: [PATCH 090/146] fix: improve error handling for Lambda invocation by capturing invocation output, status code and response body --- .../client/generate-certificate-aws-cli.sh | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index 2dc63d1..7a74149 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -1,4 +1,6 @@ #!/bin/bash + +trap 'rm -f *.json' EXIT set -eo pipefail trap 'echo "Error occurred on line $LINENO. Exiting."; exit 1;' ERR @@ -196,18 +198,33 @@ if [[ -n "$AWS_PROFILE" ]] && grep -q "$AWS_PROFILE" ${USER_AWS_DIR}/{credential AWS_PROFILE_ARG="--profile $AWS_PROFILE" fi -aws lambda invoke \ +export AWS_PAGER="" + +INVOKE_OUTPUT=$(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 \ - $AWS_PROFILE_ARG + $AWS_PROFILE_ARG 2>&1) || { + echo "$INVOKE_OUTPUT" + echo "Lambda invocation failed" + exit 1 +} response_body=$(cat response.json | jq -r ".body") || { echo "Failed to parse response body."; - exit 1; + exit 1 } +status_code=$(cat response.json | jq -r ".statusCode") || { + echo "Failed to parse status code."; + exit 1 +} + +if [[ $status_code -ne 200 ]]; then + echo "CA request failed (Status: ${status_code}): ${response_body}" + exit 1 +fi if [[ $CA_ACTION = "generateClientSSHCert" ]]; then ENCODED_CERTIFICATE=$(echo "$response_body" | jq -er ".certificate") || { @@ -259,4 +276,3 @@ fi # Clean up deactivate -rm -r *.json From e900bb95198cdcfc191874a9710b7ad709f9a4fa Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 25 Jun 2025 17:10:01 +0530 Subject: [PATCH 091/146] update secret name in deployment script, remove set -e from scripts, update module to commonJS --- private-ca/client/generate-certificate-aws-cli.sh | 2 -- private-ca/client/generate-certificate-curl.sh | 2 -- private-ca/deploy-server-on-lambda.sh | 11 ++++++++--- private-ca/server/build.mjs | 2 +- private-ca/server/package.json | 1 - private-ca/update-server-on-lambda.sh | 2 ++ 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index 7a74149..3d7750a 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -1,8 +1,6 @@ #!/bin/bash trap 'rm -f *.json' EXIT -set -eo pipefail -trap 'echo "Error occurred on line $LINENO. Exiting."; exit 1;' ERR CA_ACTION=${1:-"generateHostSSHCert"} ENVIRONMENT=${2:-"client"} diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 5241b78..8020654 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -1,6 +1,4 @@ #!/bin/bash -set -eo pipefail -trap 'echo "Error occurred on line $LINENO. Exiting."; exit 1;' ERR CA_ACTION=${1:-$CA_ACTION} CA_URL=${2:-$CA_URL} diff --git a/private-ca/deploy-server-on-lambda.sh b/private-ca/deploy-server-on-lambda.sh index 979c50e..903e8bc 100755 --- a/private-ca/deploy-server-on-lambda.sh +++ b/private-ca/deploy-server-on-lambda.sh @@ -1,14 +1,17 @@ #!/bin/bash +trap 'rm host_ca host_ca.pub user_ca user_ca.pub secret.json' EXIT -SECRET_NAME=${1:-"privateCA"} +SECRET_PREFIX=${1:-"privateCA"} ROLE_NAME=${2:-"privateCALambdaRole"} POLICY_NAME=${3:-"PrivateCAPolicy"} LAYER_NAME=${4:-"openssh"} FUNCTION_NAME=${5:-"privateCA"} -AWS_REGION=${6:-"ap-southeast-1"} -AWS_PROFILE=${7:-"default"} +AWS_REGION=${6:-"ap-southeast-12"} +AWS_PROFILE=${7:-"harshit-root"} ################## Secret ################## +ACCOUNT_ID=$(aws sts get-caller-identity --profile $AWS_PROFILE | jq -r ".Account") + # 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 "" @@ -20,6 +23,8 @@ USER_CA_PUBLIC_KEY=$(cat user_ca.pub | 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}\"}" | jq . > secret.json +SECRET_NAME="${SECRET_PREFIX}_${ACCOUNT_ID}_secret" + # Create Secret SECRET_ARN=$(aws secretsmanager create-secret \ --name $SECRET_NAME \ diff --git a/private-ca/server/build.mjs b/private-ca/server/build.mjs index d96439e..1fc724f 100644 --- a/private-ca/server/build.mjs +++ b/private-ca/server/build.mjs @@ -6,6 +6,6 @@ await build({ platform: 'node', target: 'node18', outfile: 'dist/index_lambda.js', - format: 'esm', + format: 'cjs', external: [], }); \ No newline at end of file diff --git a/private-ca/server/package.json b/private-ca/server/package.json index 04b064f..4863605 100644 --- a/private-ca/server/package.json +++ b/private-ca/server/package.json @@ -3,7 +3,6 @@ "version": "0.0.1", "description": "", "main": "index_lambda.js", - "type": "module", "scripts": { "start": "node index_lambda.js", "test": "echo \"Error: no test specified\" && exit 1", diff --git a/private-ca/update-server-on-lambda.sh b/private-ca/update-server-on-lambda.sh index 2dcabcb..e29b646 100755 --- a/private-ca/update-server-on-lambda.sh +++ b/private-ca/update-server-on-lambda.sh @@ -1,3 +1,5 @@ +#!/bin/bash + FUNCTION_NAME=${1:-'privateCA'} REGION=${2:-'ap-southeast-1'} PROFILE=${3:-'default'} From ac1f8b739e485a75e421a87f2c235e38ea55d131 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 25 Jun 2025 17:54:41 +0530 Subject: [PATCH 092/146] feat: add script and policies for creating IAM roles and policies for GitHub Actions integration --- action/setup/create-role-action.sh | 27 +++++++++++++++++++ .../setup/policies/lambda-update-policy.json | 16 +++++++++++ .../policies/trust-relationship-policy.json | 20 ++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 action/setup/create-role-action.sh create mode 100644 action/setup/policies/lambda-update-policy.json create mode 100644 action/setup/policies/trust-relationship-policy.json diff --git a/action/setup/create-role-action.sh b/action/setup/create-role-action.sh new file mode 100644 index 0000000..04a1f30 --- /dev/null +++ b/action/setup/create-role-action.sh @@ -0,0 +1,27 @@ +ACCOUNT_ID=$1 +PROFILE=$2 +POLICY_NAME=${3:-AWS_GITHUB_ACTIONS_LAMBDA_UPDATE_POLICY} +ROLE_NAME=${4:-AWS_GITHUB_ACTIONS_LAMBDA_UPDATE_ROLE} + +[ ! -z $PROFILE ] && PROFILE="--profile=$PROFILE" + +ROLE_ARN=$(aws iam list-roles --query "Roles[?RoleName=='$ROLE_NAME'].Arn" --output text $PROFILE) + +if [ -n "$ROLE_ARN" ] ; then + echo "Role $ROLE_NAME already exists" +else + ASSUME_ROLE_POLICY_DOC=$( sed "s//$ACCOUNT_ID/" policies/trust-relationship-policy.json ) + ROLE_ARN=$(aws iam create-role --role-name "$ROLE_NAME" --assume-role-policy-document "$ASSUME_ROLE_POLICY_DOC" --output text $PROFILE --query 'Role.Arn') + echo "Role created with arn: " + echo $ROLE_ARN +fi + +POLICY_ARN=$(aws iam list-policies --query "Policies[?PolicyName=='$POLICY_NAME'].Arn" --output text $PROFILE) +if [ -n "$POLICY_ARN" ]; then + echo "Policy $POLICY_NAME already exists" +else + echo "Creating Policy" + POLICY_DOC=$(sed -e "s//$ACCOUNT_ID/g" policies/lambda-update-policy.json) + POLICY_ARN=$(aws iam create-policy --policy-name $POLICY_NAME --policy-document "$POLICY_DOC" $PROFILE --output text --query 'Policy.Arn' ) + aws iam attach-role-policy --role-name "$ROLE_NAME" --policy-arn $POLICY_ARN $PROFILE +fi \ No newline at end of file diff --git a/action/setup/policies/lambda-update-policy.json b/action/setup/policies/lambda-update-policy.json new file mode 100644 index 0000000..c930e92 --- /dev/null +++ b/action/setup/policies/lambda-update-policy.json @@ -0,0 +1,16 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "lambda:UpdateFunctionCode", + "lambda:GetFunction", + "lambda:ListFunctions" + ], + "Resource": [ + "arn:aws:lambda:*::function:privateCA" + ] + } + ] +} \ No newline at end of file diff --git a/action/setup/policies/trust-relationship-policy.json b/action/setup/policies/trust-relationship-policy.json new file mode 100644 index 0000000..acc2b63 --- /dev/null +++ b/action/setup/policies/trust-relationship-policy.json @@ -0,0 +1,20 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "arn:aws:iam:::oidc-provider/token.actions.githubusercontent.com" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" + }, + "StringLike": { + "token.actions.githubusercontent.com:sub": "repo:*/network-utils:environment:Prod" + } + } + } + ] +} \ No newline at end of file From 2f91f4dba2a47bce07999661f4aff0694465f9cd Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 25 Jun 2025 18:40:15 +0530 Subject: [PATCH 093/146] chore: update IAM role and policy names in create-role-action and in workflow --- .github/workflows/update-private-ca-lambda.yml | 2 +- action/setup/create-role-action.sh | 4 ++-- action/setup/policies/lambda-update-policy.json | 3 +-- action/setup/policies/trust-relationship-policy.json | 2 +- private-ca/server/generate-host-ssh-cert.js | 7 ++++++- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/update-private-ca-lambda.yml b/.github/workflows/update-private-ca-lambda.yml index dffd04d..9096267 100644 --- a/.github/workflows/update-private-ca-lambda.yml +++ b/.github/workflows/update-private-ca-lambda.yml @@ -24,7 +24,7 @@ jobs: uses: aws-actions/configure-aws-credentials@v4 with: aws-region: ${{ secrets.AWS_REGION }} - role-to-assume: ${{ secrets.AWS_SSH_UPDATE_ROLE_ARN }} + role-to-assume: ${{ secrets.AWS_PRIVATE_CA_LAMBDA_UPDATE_ROLE }} role-session-name: UpdatePrivateCALambda - name: Run update-server-on-lambda.sh diff --git a/action/setup/create-role-action.sh b/action/setup/create-role-action.sh index 04a1f30..7d9e7cd 100644 --- a/action/setup/create-role-action.sh +++ b/action/setup/create-role-action.sh @@ -1,7 +1,7 @@ ACCOUNT_ID=$1 PROFILE=$2 -POLICY_NAME=${3:-AWS_GITHUB_ACTIONS_LAMBDA_UPDATE_POLICY} -ROLE_NAME=${4:-AWS_GITHUB_ACTIONS_LAMBDA_UPDATE_ROLE} +POLICY_NAME=${3:-AWS_PRIVATE_CA_LAMBDA_UPDATE_POLICY} +ROLE_NAME=${4:-AWS_PRIVATE_CA_LAMBDA_UPDATE_ROLE} [ ! -z $PROFILE ] && PROFILE="--profile=$PROFILE" diff --git a/action/setup/policies/lambda-update-policy.json b/action/setup/policies/lambda-update-policy.json index c930e92..8f0c38d 100644 --- a/action/setup/policies/lambda-update-policy.json +++ b/action/setup/policies/lambda-update-policy.json @@ -5,8 +5,7 @@ "Effect": "Allow", "Action": [ "lambda:UpdateFunctionCode", - "lambda:GetFunction", - "lambda:ListFunctions" + "lambda:GetFunction" ], "Resource": [ "arn:aws:lambda:*::function:privateCA" diff --git a/action/setup/policies/trust-relationship-policy.json b/action/setup/policies/trust-relationship-policy.json index acc2b63..f3a7040 100644 --- a/action/setup/policies/trust-relationship-policy.json +++ b/action/setup/policies/trust-relationship-policy.json @@ -12,7 +12,7 @@ "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" }, "StringLike": { - "token.actions.githubusercontent.com:sub": "repo:*/network-utils:environment:Prod" + "token.actions.githubusercontent.com:sub": "repo:getfundwave/network-utils:environment:Prod" } } } diff --git a/private-ca/server/generate-host-ssh-cert.js b/private-ca/server/generate-host-ssh-cert.js index cf4feed..ea41851 100644 --- a/private-ca/server/generate-host-ssh-cert.js +++ b/private-ca/server/generate-host-ssh-cert.js @@ -29,7 +29,12 @@ export const signHostSSHCertificate = async (callerIdentity, secret, certPubkey, console.log('stdout:', stdout); console.log('stderr:', stderr); - ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I host_${instanceId} -h -n ${publicIp} -V +1d ${publicKeyPath}`)); + ( + { stdout, stderr } = await exec( + `ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I host_${instanceId} -h -n ${publicIp} -V +1d ${publicKeyPath}` + ) + ); + console.log('stdout:', stdout); console.log('stderr:', stderr); From 79b469b620576e3d1fa93af0f47ba015010c8335 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Mon, 30 Jun 2025 15:59:05 +0530 Subject: [PATCH 094/146] fix: changed AWS_PROFILE_ARG logic from credentials/config check to environment check --- private-ca/client/generate-certificate-aws-cli.sh | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index 3d7750a..18ef124 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -44,6 +44,7 @@ get_aws_credentials() { else TEMP_CREDS=$(aws sts get-session-token --profile $AWS_PROFILE | jq -r ".Credentials") fi + AWS_PROFILE_ARG="--profile $AWS_PROFILE" else echo "Invalid environment provided. Allowed values are 'host' and 'client'"; exit 1; fi @@ -188,14 +189,6 @@ INNER_JSON=$(jq -n \ json_body=$(jq -n --arg body "$INNER_JSON" '{body: $body}') echo "$json_body" > event.json -# Use --profile only if AWS_PROFILE is set and ~/.aws/credentials exists -# On EC2 instances with IAM roles, credentials are fetched from instance metadata, -# and using --profile will cause an error if ~/.aws/credentials doesn't exist. -AWS_PROFILE_ARG="" -if [[ -n "$AWS_PROFILE" ]] && grep -q "$AWS_PROFILE" ${USER_AWS_DIR}/{credentials,config} 2>/dev/null; then - AWS_PROFILE_ARG="--profile $AWS_PROFILE" -fi - export AWS_PAGER="" INVOKE_OUTPUT=$(aws lambda invoke \ From ff403a53ecd852d350da5dbdf0599d206fa1786f Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 2 Jul 2025 11:34:31 +0530 Subject: [PATCH 095/146] feat: add script to retrieve authorized principals from AWS EC2 metadata, update client cert generate function to add principals from secret --- private-ca/get-authorized-principals.sh | 15 +++++++++++++++ private-ca/server/generate-client-ssh-cert.js | 7 ++++++- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 private-ca/get-authorized-principals.sh diff --git a/private-ca/get-authorized-principals.sh b/private-ca/get-authorized-principals.sh new file mode 100644 index 0000000..3697d87 --- /dev/null +++ b/private-ca/get-authorized-principals.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") + +INSTANCE_ID=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id) + +REGION=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region) + +ENV_TAG=$(aws ec2 describe-tags \ + --region "$REGION" \ + --filters "Name=resource-id,Values=$INSTANCE_ID" "Name=key,Values=ENV" \ + --query "Tags[0].Value" \ + --output text) + +echo $ENV_TAG \ No newline at end of file diff --git a/private-ca/server/generate-client-ssh-cert.js b/private-ca/server/generate-client-ssh-cert.js index 7cae43c..9899155 100644 --- a/private-ca/server/generate-client-ssh-cert.js +++ b/private-ca/server/generate-client-ssh-cert.js @@ -27,9 +27,14 @@ export const signClientSSHCertificate = async (callerIdentity, secret, certPubke console.log('stdout:', stdout); console.log('stderr:', stderr); + const principalList = [ + roleName, + ...secret[roleName].split(',').map(s => s.trim()) + ].join(','); + ( { stdout, stderr } = await exec( - `ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I client_${roleName} -n ${roleName} -V +${validityInDays}d ${publicKeyPath}` + `ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I client_${roleName} -n ${principalList} -V +${validityInDays}d ${publicKeyPath}` ) ); From a6b5d90493a383fdd761edf13fb774e7b4b19f74 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 2 Jul 2025 12:01:03 +0530 Subject: [PATCH 096/146] feat: add date formatting utility and update SSH cert generation to use absolute validity period --- private-ca/server/format-date.js | 12 ++++++++++++ private-ca/server/generate-client-ssh-cert.js | 13 +++++++++++-- private-ca/server/generate-host-ssh-cert.js | 12 +++++++++++- 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 private-ca/server/format-date.js diff --git a/private-ca/server/format-date.js b/private-ca/server/format-date.js new file mode 100644 index 0000000..d2bbd6d --- /dev/null +++ b/private-ca/server/format-date.js @@ -0,0 +1,12 @@ +const formatDate = (date) => { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + const seconds = String(date.getSeconds()).padStart(2, '0'); + + return `${year}${month}${day}${hours}${minutes}${seconds}`; +}; + +export default formatDate; \ No newline at end of file diff --git a/private-ca/server/generate-client-ssh-cert.js b/private-ca/server/generate-client-ssh-cert.js index 9899155..e19cc55 100644 --- a/private-ca/server/generate-client-ssh-cert.js +++ b/private-ca/server/generate-client-ssh-cert.js @@ -1,6 +1,7 @@ import fs from 'fs'; import child_process from 'child_process'; import util from 'util'; +import formatDate from './format-date.js'; const exec = util.promisify(child_process.exec); const validityInDays = process.env.validityInDays ?? 1; @@ -32,9 +33,17 @@ export const signClientSSHCertificate = async (callerIdentity, secret, certPubke ...secret[roleName].split(',').map(s => s.trim()) ].join(','); + const now = new Date(); + const validFrom = formatDate(now); + + const validUntil = new Date(now.getTime() + (validityInDays * 24 * 60 * 60 * 1000)); + const validTo = formatDate(validUntil); + + const validityPeriod = `${validFrom}:${validTo}`; + ( { stdout, stderr } = await exec( - `ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I client_${roleName} -n ${principalList} -V +${validityInDays}d ${publicKeyPath}` + `ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I client_${roleName} -n ${principalList} -V ${validityPeriod} ${publicKeyPath}` ) ); @@ -44,4 +53,4 @@ export const signClientSSHCertificate = async (callerIdentity, secret, certPubke const certificate = fs.readFileSync(certificatePath, 'utf8'); return certificate; -}; +}; \ No newline at end of file diff --git a/private-ca/server/generate-host-ssh-cert.js b/private-ca/server/generate-host-ssh-cert.js index ea41851..db7aff8 100644 --- a/private-ca/server/generate-host-ssh-cert.js +++ b/private-ca/server/generate-host-ssh-cert.js @@ -2,8 +2,10 @@ import fs from 'fs'; import child_process from 'child_process'; import util from 'util'; import { getPublicIpAddress } from './get-public-ip-address.js'; +import formatDate from './format-date.js'; const exec = util.promisify(child_process.exec); +const validityInDays = process.env.validityInDays ?? 1; export const signHostSSHCertificate = async (callerIdentity, secret, certPubkey, awsEC2Region) => { @@ -29,9 +31,17 @@ export const signHostSSHCertificate = async (callerIdentity, secret, certPubkey, console.log('stdout:', stdout); console.log('stderr:', stderr); + const now = new Date(); + const validFrom = formatDate(now); + + const validUntil = new Date(now.getTime() + (validityInDays * 24 * 60 * 60 * 1000)); + const validTo = formatDate(validUntil); + + const validityPeriod = `${validFrom}:${validTo}`; + ( { stdout, stderr } = await exec( - `ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I host_${instanceId} -h -n ${publicIp} -V +1d ${publicKeyPath}` + `ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I host_${instanceId} -h -n ${publicIp} -V ${validityPeriod} ${publicKeyPath}` ) ); From fe0b3c89df8c373f36cfe84b785b188e42067ee2 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 2 Jul 2025 17:57:17 +0530 Subject: [PATCH 097/146] fix [major]: remove RBAC using ENV tag of EC2 hosts and revert SSH cert generation to only list role name as principal --- private-ca/get-authorized-principals.sh | 15 --------------- private-ca/server/generate-client-ssh-cert.js | 7 +------ 2 files changed, 1 insertion(+), 21 deletions(-) delete mode 100644 private-ca/get-authorized-principals.sh diff --git a/private-ca/get-authorized-principals.sh b/private-ca/get-authorized-principals.sh deleted file mode 100644 index 3697d87..0000000 --- a/private-ca/get-authorized-principals.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") - -INSTANCE_ID=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id) - -REGION=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region) - -ENV_TAG=$(aws ec2 describe-tags \ - --region "$REGION" \ - --filters "Name=resource-id,Values=$INSTANCE_ID" "Name=key,Values=ENV" \ - --query "Tags[0].Value" \ - --output text) - -echo $ENV_TAG \ No newline at end of file diff --git a/private-ca/server/generate-client-ssh-cert.js b/private-ca/server/generate-client-ssh-cert.js index e19cc55..4c3840a 100644 --- a/private-ca/server/generate-client-ssh-cert.js +++ b/private-ca/server/generate-client-ssh-cert.js @@ -28,11 +28,6 @@ export const signClientSSHCertificate = async (callerIdentity, secret, certPubke console.log('stdout:', stdout); console.log('stderr:', stderr); - const principalList = [ - roleName, - ...secret[roleName].split(',').map(s => s.trim()) - ].join(','); - const now = new Date(); const validFrom = formatDate(now); @@ -43,7 +38,7 @@ export const signClientSSHCertificate = async (callerIdentity, secret, certPubke ( { stdout, stderr } = await exec( - `ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I client_${roleName} -n ${principalList} -V ${validityPeriod} ${publicKeyPath}` + `ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I client_${roleName} -n ${roleName} -V ${validityPeriod} ${publicKeyPath}` ) ); From 1de518e1d3f1bea51dae4998e542dc11e17f9790 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Thu, 3 Jul 2025 13:33:56 +0530 Subject: [PATCH 098/146] refactor: migrate to TypeScript, restructure project - removed AWS_SECRETS_REGION env var --- private-ca/server/build.mjs | 3 +- private-ca/server/get-caller-identity.js | 54 -- private-ca/server/index_lambda.js | 73 --- private-ca/server/package-lock.json | 481 ++++++++++-------- private-ca/server/package.json | 9 +- private-ca/server/secret-manager-utils.js | 14 - .../{format-date.js => src/format-date.ts} | 4 +- .../generate-client-ssh-cert.ts} | 33 +- .../generate-host-ssh-cert.ts} | 38 +- private-ca/server/src/get-caller-identity.ts | 53 ++ .../get-public-ip-address.ts} | 11 +- private-ca/server/src/index_lambda.ts | 93 ++++ private-ca/server/src/secret-manager-utils.ts | 25 + private-ca/server/src/types/index.ts | 36 ++ private-ca/server/tsconfig.json | 23 + 15 files changed, 543 insertions(+), 407 deletions(-) delete mode 100644 private-ca/server/get-caller-identity.js delete mode 100644 private-ca/server/index_lambda.js delete mode 100644 private-ca/server/secret-manager-utils.js rename private-ca/server/{format-date.js => src/format-date.ts} (84%) rename private-ca/server/{generate-client-ssh-cert.js => src/generate-client-ssh-cert.ts} (60%) rename private-ca/server/{generate-host-ssh-cert.js => src/generate-host-ssh-cert.ts} (56%) create mode 100644 private-ca/server/src/get-caller-identity.ts rename private-ca/server/{get-public-ip-address.js => src/get-public-ip-address.ts} (67%) create mode 100644 private-ca/server/src/index_lambda.ts create mode 100644 private-ca/server/src/secret-manager-utils.ts create mode 100644 private-ca/server/src/types/index.ts create mode 100644 private-ca/server/tsconfig.json diff --git a/private-ca/server/build.mjs b/private-ca/server/build.mjs index 1fc724f..036a195 100644 --- a/private-ca/server/build.mjs +++ b/private-ca/server/build.mjs @@ -1,11 +1,10 @@ import { build } from 'esbuild'; await build({ - entryPoints: ['index_lambda.js'], + entryPoints: ['src/index_lambda.ts'], bundle: true, platform: 'node', target: 'node18', outfile: 'dist/index_lambda.js', format: 'cjs', - external: [], }); \ No newline at end of file diff --git a/private-ca/server/get-caller-identity.js b/private-ca/server/get-caller-identity.js deleted file mode 100644 index 18aa180..0000000 --- a/private-ca/server/get-caller-identity.js +++ /dev/null @@ -1,54 +0,0 @@ -import https from 'https'; - -export const getCallerIdentity = (event) => { - - 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 = { - 'accept': 'application/json', - 'Content-Type': 'application/x-www-form-urlencoded', - 'X-Amz-Date': auth.amzDate, - 'Authorization': auth.authorizationHeader, - 'X-Amz-Security-Token': auth.sessionToken, - 'Aud': 'FundwaveCA' - }; - - 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) => { - reject(new Error(error)); - }); - - req.write(payload); - req.end(); - }); - -}; \ No newline at end of file diff --git a/private-ca/server/index_lambda.js b/private-ca/server/index_lambda.js deleted file mode 100644 index ba9972d..0000000 --- a/private-ca/server/index_lambda.js +++ /dev/null @@ -1,73 +0,0 @@ -import { signHostSSHCertificate } from './generate-host-ssh-cert.js'; -import { signClientSSHCertificate } from './generate-client-ssh-cert.js'; -import { getCallerIdentity } from './get-caller-identity.js'; -import { getSecret } from './secret-manager-utils.js'; - -const AWS_SECRETS_REGION = process.env.AWS_SECRETS_REGION; - -export const handler = async (event) => { - try { - event = JSON.parse(event.body); - - if (!event.certPubkey) { - return { - statusCode: 400, - body: JSON.stringify({ error: 'Missing certPubkey' }), - }; - } - - // auth - const callerIdentity = await getCallerIdentity(event); - const accountId = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Account; - - // secret - const secret = await getSecret(AWS_SECRETS_REGION, 'privateCA', accountId); - if (!secret) { - return { - statusCode: 401, - body: JSON.stringify({ error: 'This AWS account is not configured to use this service'}), - }; - } - - // action - switch (event.action) { - case "generateHostSSHCert": - if (!event.awsEC2Region) { - return { - statusCode: 400, - body: JSON.stringify({ error: 'Missing awsEC2Region' }), - }; - } - - const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.certPubkey, event.awsEC2Region); - return { - statusCode: 200, - body: JSON.stringify({ - "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({ - "certificate": Buffer.from(clientSSHCert).toString('base64'), - "host_ca.pub": secret["host_ca.pub"] - }) - }; - default: - console.log("Invalid Action") - return { - statusCode: 400, - body: JSON.stringify({ error: 'Invalid Action' }), - }; - } - } catch (err) { - console.error('Error in cert signing handler:', err); - return { - statusCode: 500, - body: JSON.stringify({ error: 'Internal server error' }), - }; - } -}; \ No newline at end of file diff --git a/private-ca/server/package-lock.json b/private-ca/server/package-lock.json index 73ab659..934d8a1 100644 --- a/private-ca/server/package-lock.json +++ b/private-ca/server/package-lock.json @@ -14,7 +14,9 @@ "node-forge": "^1.3.1" }, "devDependencies": { - "esbuild": "^0.25.5" + "@types/node": "^20.10.5", + "esbuild": "^0.25.5", + "typescript": "^5.3.3" } }, "node_modules/@aws-crypto/sha256-browser": { @@ -143,51 +145,51 @@ } }, "node_modules/@aws-sdk/client-ec2": { - "version": "3.836.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-ec2/-/client-ec2-3.836.0.tgz", - "integrity": "sha512-G+P6689zpW8scOC02uG7SeLyx8aHywa9EJgVEmzc0PGmjVveUit8VjBETF7L0Bt3ejk2voWWdyBFTDDKHf8flQ==", + "version": "3.842.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-ec2/-/client-ec2-3.842.0.tgz", + "integrity": "sha512-TTQYEOaVYkVm+hvIMkfmbSJfS5739ya1pJV1XyBTceegzyAyUcchXh7x0AVY8X69QpOqKCksY3NQEXjby3n9gg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/credential-provider-node": "3.835.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-sdk-ec2": "3.835.0", - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.835.0", + "@aws-sdk/core": "3.840.0", + "@aws-sdk/credential-provider-node": "3.840.0", + "@aws-sdk/middleware-host-header": "3.840.0", + "@aws-sdk/middleware-logger": "3.840.0", + "@aws-sdk/middleware-recursion-detection": "3.840.0", + "@aws-sdk/middleware-sdk-ec2": "3.840.0", + "@aws-sdk/middleware-user-agent": "3.840.0", + "@aws-sdk/region-config-resolver": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.840.0", + "@aws-sdk/util-user-agent-browser": "3.840.0", + "@aws-sdk/util-user-agent-node": "3.840.0", "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", + "@smithy/core": "^3.6.0", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.12", - "@smithy/middleware-retry": "^4.1.13", + "@smithy/middleware-endpoint": "^4.1.13", + "@smithy/middleware-retry": "^4.1.14", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", + "@smithy/smithy-client": "^4.4.5", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.20", - "@smithy/util-defaults-mode-node": "^4.0.20", + "@smithy/util-defaults-mode-browser": "^4.0.21", + "@smithy/util-defaults-mode-node": "^4.0.21", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.6", "@smithy/util-utf8": "^4.0.0", - "@smithy/util-waiter": "^4.0.5", + "@smithy/util-waiter": "^4.0.6", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" @@ -197,45 +199,45 @@ } }, "node_modules/@aws-sdk/client-secrets-manager": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.835.0.tgz", - "integrity": "sha512-w8xIFhxP54kRdmTuRjxOAgNU7MCSgVieXx5pUxMD6B92dpqDTjnVFgTDX8fpUFZSrSwe5dOCiHEDKZsV20YNaQ==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.840.0.tgz", + "integrity": "sha512-oUcoZT4YJc/WUoxydfzSE3o89dBvdzan75XOLXg3JVg64os4ao8SUkIphR3YXmjmHz8qwaVNXVF4MpR3IxGPCg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/credential-provider-node": "3.835.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.835.0", + "@aws-sdk/core": "3.840.0", + "@aws-sdk/credential-provider-node": "3.840.0", + "@aws-sdk/middleware-host-header": "3.840.0", + "@aws-sdk/middleware-logger": "3.840.0", + "@aws-sdk/middleware-recursion-detection": "3.840.0", + "@aws-sdk/middleware-user-agent": "3.840.0", + "@aws-sdk/region-config-resolver": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.840.0", + "@aws-sdk/util-user-agent-browser": "3.840.0", + "@aws-sdk/util-user-agent-node": "3.840.0", "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", + "@smithy/core": "^3.6.0", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.12", - "@smithy/middleware-retry": "^4.1.13", + "@smithy/middleware-endpoint": "^4.1.13", + "@smithy/middleware-retry": "^4.1.14", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", + "@smithy/smithy-client": "^4.4.5", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.20", - "@smithy/util-defaults-mode-node": "^4.0.20", + "@smithy/util-defaults-mode-browser": "^4.0.21", + "@smithy/util-defaults-mode-node": "^4.0.21", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.6", @@ -249,44 +251,44 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.835.0.tgz", - "integrity": "sha512-4J19IcBKU5vL8yw/YWEvbwEGcmCli0rpRyxG53v0K5/3weVPxVBbKfkWcjWVQ4qdxNz2uInfbTde4BRBFxWllQ==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.840.0.tgz", + "integrity": "sha512-3Zp+FWN2hhmKdpS0Ragi5V2ZPsZNScE3jlbgoJjzjI/roHZqO+e3/+XFN4TlM0DsPKYJNp+1TAjmhxN6rOnfYA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.835.0", + "@aws-sdk/core": "3.840.0", + "@aws-sdk/middleware-host-header": "3.840.0", + "@aws-sdk/middleware-logger": "3.840.0", + "@aws-sdk/middleware-recursion-detection": "3.840.0", + "@aws-sdk/middleware-user-agent": "3.840.0", + "@aws-sdk/region-config-resolver": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.840.0", + "@aws-sdk/util-user-agent-browser": "3.840.0", + "@aws-sdk/util-user-agent-node": "3.840.0", "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", + "@smithy/core": "^3.6.0", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.12", - "@smithy/middleware-retry": "^4.1.13", + "@smithy/middleware-endpoint": "^4.1.13", + "@smithy/middleware-retry": "^4.1.14", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", + "@smithy/smithy-client": "^4.4.5", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.20", - "@smithy/util-defaults-mode-node": "^4.0.20", + "@smithy/util-defaults-mode-browser": "^4.0.21", + "@smithy/util-defaults-mode-node": "^4.0.21", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.6", @@ -298,19 +300,19 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.835.0.tgz", - "integrity": "sha512-7mnf4xbaLI8rkDa+w6fUU48dG6yDuOgLXEPe4Ut3SbMp1ceJBPMozNHbCwkiyHk3HpxZYf8eVy0wXhJMrxZq5w==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.840.0.tgz", + "integrity": "sha512-x3Zgb39tF1h2XpU+yA4OAAQlW6LVEfXNlSedSYJ7HGKXqA/E9h3rWQVpYfhXXVVsLdYXdNw5KBUkoAoruoZSZA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", + "@aws-sdk/types": "3.840.0", "@aws-sdk/xml-builder": "3.821.0", - "@smithy/core": "^3.5.3", + "@smithy/core": "^3.6.0", "@smithy/node-config-provider": "^4.1.3", "@smithy/property-provider": "^4.0.4", "@smithy/protocol-http": "^5.1.2", "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", + "@smithy/smithy-client": "^4.4.5", "@smithy/types": "^4.3.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", @@ -324,13 +326,13 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.835.0.tgz", - "integrity": "sha512-U9LFWe7+ephNyekpUbzT7o6SmJTmn6xkrPkE0D7pbLojnPVi/8SZKyjtgQGIsAv+2kFkOCqMOIYUKd/0pE7uew==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.840.0.tgz", + "integrity": "sha512-EzF6VcJK7XvQ/G15AVEfJzN2mNXU8fcVpXo4bRyr1S6t2q5zx6UPH/XjDbn18xyUmOq01t+r8gG+TmHEVo18fA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", + "@aws-sdk/core": "3.840.0", + "@aws-sdk/types": "3.840.0", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -340,18 +342,18 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.835.0.tgz", - "integrity": "sha512-jCdNEsQklil7frDm/BuVKl4ubVoQHRbV6fnkOjmxAJz0/v7cR8JP0jBGlqKKzh3ROh5/vo1/5VUZbCTLpc9dSg==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.840.0.tgz", + "integrity": "sha512-wbnUiPGLVea6mXbUh04fu+VJmGkQvmToPeTYdHE8eRZq3NRDi3t3WltT+jArLBKD/4NppRpMjf2ju4coMCz91g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", + "@aws-sdk/core": "3.840.0", + "@aws-sdk/types": "3.840.0", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/node-http-handler": "^4.0.6", "@smithy/property-provider": "^4.0.4", "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", + "@smithy/smithy-client": "^4.4.5", "@smithy/types": "^4.3.1", "@smithy/util-stream": "^4.2.2", "tslib": "^2.6.2" @@ -361,19 +363,19 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.835.0.tgz", - "integrity": "sha512-nqF6rYRAnJedmvDfrfKygzyeADcduDvtvn7GlbQQbXKeR2l7KnCdhuxHa0FALLvspkHiBx7NtInmvnd5IMuWsw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/credential-provider-env": "3.835.0", - "@aws-sdk/credential-provider-http": "3.835.0", - "@aws-sdk/credential-provider-process": "3.835.0", - "@aws-sdk/credential-provider-sso": "3.835.0", - "@aws-sdk/credential-provider-web-identity": "3.835.0", - "@aws-sdk/nested-clients": "3.835.0", - "@aws-sdk/types": "3.821.0", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.840.0.tgz", + "integrity": "sha512-7F290BsWydShHb+7InXd+IjJc3mlEIm9I0R57F/Pjl1xZB69MdkhVGCnuETWoBt4g53ktJd6NEjzm/iAhFXFmw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.840.0", + "@aws-sdk/credential-provider-env": "3.840.0", + "@aws-sdk/credential-provider-http": "3.840.0", + "@aws-sdk/credential-provider-process": "3.840.0", + "@aws-sdk/credential-provider-sso": "3.840.0", + "@aws-sdk/credential-provider-web-identity": "3.840.0", + "@aws-sdk/nested-clients": "3.840.0", + "@aws-sdk/types": "3.840.0", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", @@ -385,18 +387,18 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.835.0.tgz", - "integrity": "sha512-77B8elyZlaEd7vDYyCnYtVLuagIBwuJ0AQ98/36JMGrYX7TT8UVAhiDAfVe0NdUOMORvDNFfzL06VBm7wittYw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.835.0", - "@aws-sdk/credential-provider-http": "3.835.0", - "@aws-sdk/credential-provider-ini": "3.835.0", - "@aws-sdk/credential-provider-process": "3.835.0", - "@aws-sdk/credential-provider-sso": "3.835.0", - "@aws-sdk/credential-provider-web-identity": "3.835.0", - "@aws-sdk/types": "3.821.0", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.840.0.tgz", + "integrity": "sha512-KufP8JnxA31wxklLm63evUPSFApGcH8X86z3mv9SRbpCm5ycgWIGVCTXpTOdgq6rPZrwT9pftzv2/b4mV/9clg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.840.0", + "@aws-sdk/credential-provider-http": "3.840.0", + "@aws-sdk/credential-provider-ini": "3.840.0", + "@aws-sdk/credential-provider-process": "3.840.0", + "@aws-sdk/credential-provider-sso": "3.840.0", + "@aws-sdk/credential-provider-web-identity": "3.840.0", + "@aws-sdk/types": "3.840.0", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", @@ -408,13 +410,13 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.835.0.tgz", - "integrity": "sha512-qXkTt5pAhSi2Mp9GdgceZZFo/cFYrA735efqi/Re/nf0lpqBp8mRM8xv+iAaPHV4Q10q0DlkbEidT1DhxdT/+w==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.840.0.tgz", + "integrity": "sha512-HkDQWHy8tCI4A0Ps2NVtuVYMv9cB4y/IuD/TdOsqeRIAT12h8jDb98BwQPNLAImAOwOWzZJ8Cu0xtSpX7CQhMw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", + "@aws-sdk/core": "3.840.0", + "@aws-sdk/types": "3.840.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", @@ -425,15 +427,15 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.835.0.tgz", - "integrity": "sha512-jAiEMryaPFXayYGszrc7NcgZA/zrrE3QvvvUBh/Udasg+9Qp5ZELdJCm/p98twNyY9n5i6Ex6VgvdxZ7+iEheQ==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.840.0.tgz", + "integrity": "sha512-2qgdtdd6R0Z1y0KL8gzzwFUGmhBHSUx4zy85L2XV1CXhpRNwV71SVWJqLDVV5RVWVf9mg50Pm3AWrUC0xb0pcA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.835.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/token-providers": "3.835.0", - "@aws-sdk/types": "3.821.0", + "@aws-sdk/client-sso": "3.840.0", + "@aws-sdk/core": "3.840.0", + "@aws-sdk/token-providers": "3.840.0", + "@aws-sdk/types": "3.840.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", @@ -444,14 +446,14 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.835.0.tgz", - "integrity": "sha512-zfleEFXDLlcJ7cyfS4xSyCRpd8SVlYZfH3rp0pg2vPYKbnmXVE0r+gPIYXl4L+Yz4A2tizYl63nKCNdtbxadog==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.840.0.tgz", + "integrity": "sha512-dpEeVXG8uNZSmVXReE4WP0lwoioX2gstk4RnUgrdUE3YaPq8A+hJiVAyc3h+cjDeIqfbsQbZm9qFetKC2LF9dQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/nested-clients": "3.835.0", - "@aws-sdk/types": "3.821.0", + "@aws-sdk/core": "3.840.0", + "@aws-sdk/nested-clients": "3.840.0", + "@aws-sdk/types": "3.840.0", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -461,12 +463,12 @@ } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.821.0.tgz", - "integrity": "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.840.0.tgz", + "integrity": "sha512-ub+hXJAbAje94+Ya6c6eL7sYujoE8D4Bumu1NUI8TXjUhVVn0HzVWQjpRLshdLsUp1AW7XyeJaxyajRaJQ8+Xg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", + "@aws-sdk/types": "3.840.0", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -476,12 +478,12 @@ } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.821.0.tgz", - "integrity": "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.840.0.tgz", + "integrity": "sha512-lSV8FvjpdllpGaRspywss4CtXV8M7NNNH+2/j86vMH+YCOZ6fu2T/TyFd/tHwZ92vDfHctWkRbQxg0bagqwovA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", + "@aws-sdk/types": "3.840.0", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, @@ -490,12 +492,12 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.821.0.tgz", - "integrity": "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.840.0.tgz", + "integrity": "sha512-Gu7lGDyfddyhIkj1Z1JtrY5NHb5+x/CRiB87GjaSrKxkDaydtX2CU977JIABtt69l9wLbcGDIQ+W0uJ5xPof7g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", + "@aws-sdk/types": "3.840.0", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -505,17 +507,17 @@ } }, "node_modules/@aws-sdk/middleware-sdk-ec2": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-ec2/-/middleware-sdk-ec2-3.835.0.tgz", - "integrity": "sha512-wepCXGo+rsxpOPxZcInyyesCQJ21nJjX6ldRs6ige/NeaDQlLjOOsbQCXUT/b4xoFFjUxUUG6zZHudK7jFzgIA==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-ec2/-/middleware-sdk-ec2-3.840.0.tgz", + "integrity": "sha512-TVYRq3NNq+Cb4N5jODASOmKwPBa4zXH0CT5Ifrav+fH7SVtkfXurVMkLaAu1zFHyllQgAQ6O4O/MpwDq2H1nkw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-format-url": "3.821.0", - "@smithy/middleware-endpoint": "^4.1.12", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-format-url": "3.840.0", + "@smithy/middleware-endpoint": "^4.1.13", "@smithy/protocol-http": "^5.1.2", "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", + "@smithy/smithy-client": "^4.4.5", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, @@ -524,15 +526,15 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.835.0.tgz", - "integrity": "sha512-2gmAYygeE/gzhyF2XlkcbMLYFTbNfV61n+iCFa/ZofJHXYE+RxSyl5g4kujLEs7bVZHmjQZJXhprVSkGccq3/w==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.840.0.tgz", + "integrity": "sha512-hiiMf7BP5ZkAFAvWRcK67Mw/g55ar7OCrvrynC92hunx/xhMkrgSLM0EXIZ1oTn3uql9kH/qqGF0nqsK6K555A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@smithy/core": "^3.5.3", + "@aws-sdk/core": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.840.0", + "@smithy/core": "^3.6.0", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -542,44 +544,44 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.835.0.tgz", - "integrity": "sha512-UtmOO0U5QkicjCEv+B32qqRAnS7o2ZkZhC+i3ccH1h3fsfaBshpuuNBwOYAzRCRBeKW5fw3ANFrV/+2FTp4jWg==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.840.0.tgz", + "integrity": "sha512-LXYYo9+n4hRqnRSIMXLBb+BLz+cEmjMtTudwK1BF6Bn2RfdDv29KuyeDRrPCS3TwKl7ZKmXUmE9n5UuHAPfBpA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.835.0", + "@aws-sdk/core": "3.840.0", + "@aws-sdk/middleware-host-header": "3.840.0", + "@aws-sdk/middleware-logger": "3.840.0", + "@aws-sdk/middleware-recursion-detection": "3.840.0", + "@aws-sdk/middleware-user-agent": "3.840.0", + "@aws-sdk/region-config-resolver": "3.840.0", + "@aws-sdk/types": "3.840.0", + "@aws-sdk/util-endpoints": "3.840.0", + "@aws-sdk/util-user-agent-browser": "3.840.0", + "@aws-sdk/util-user-agent-node": "3.840.0", "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", + "@smithy/core": "^3.6.0", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.12", - "@smithy/middleware-retry": "^4.1.13", + "@smithy/middleware-endpoint": "^4.1.13", + "@smithy/middleware-retry": "^4.1.14", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", + "@smithy/smithy-client": "^4.4.5", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.20", - "@smithy/util-defaults-mode-node": "^4.0.20", + "@smithy/util-defaults-mode-browser": "^4.0.21", + "@smithy/util-defaults-mode-node": "^4.0.21", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.6", @@ -591,12 +593,12 @@ } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.821.0.tgz", - "integrity": "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.840.0.tgz", + "integrity": "sha512-Qjnxd/yDv9KpIMWr90ZDPtRj0v75AqGC92Lm9+oHXZ8p1MjG5JE2CW0HL8JRgK9iKzgKBL7pPQRXI8FkvEVfrA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", + "@aws-sdk/types": "3.840.0", "@smithy/node-config-provider": "^4.1.3", "@smithy/types": "^4.3.1", "@smithy/util-config-provider": "^4.0.0", @@ -608,14 +610,14 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.835.0.tgz", - "integrity": "sha512-zN1P3BE+Rv7w7q/CDA8VCQox6SE9QTn0vDtQ47AHA3eXZQQgYzBqgoLgJxR9rKKBIRGZqInJa/VRskLL95VliQ==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.840.0.tgz", + "integrity": "sha512-6BuTOLTXvmgwjK7ve7aTg9JaWFdM5UoMolLVPMyh3wTv9Ufalh8oklxYHUBIxsKkBGO2WiHXytveuxH6tAgTYg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/nested-clients": "3.835.0", - "@aws-sdk/types": "3.821.0", + "@aws-sdk/core": "3.840.0", + "@aws-sdk/nested-clients": "3.840.0", + "@aws-sdk/types": "3.840.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", @@ -626,9 +628,9 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.821.0.tgz", - "integrity": "sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.840.0.tgz", + "integrity": "sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.3.1", @@ -639,12 +641,12 @@ } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.828.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.828.0.tgz", - "integrity": "sha512-RvKch111SblqdkPzg3oCIdlGxlQs+k+P7Etory9FmxPHyPDvsP1j1c74PmgYqtzzMWmoXTjd+c9naUHh9xG8xg==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.840.0.tgz", + "integrity": "sha512-eqE9ROdg/Kk0rj3poutyRCFauPDXIf/WSvCqFiRDDVi6QOnCv/M0g2XW8/jSvkJlOyaXkNCptapIp6BeeFFGYw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", + "@aws-sdk/types": "3.840.0", "@smithy/types": "^4.3.1", "@smithy/util-endpoints": "^3.0.6", "tslib": "^2.6.2" @@ -654,12 +656,12 @@ } }, "node_modules/@aws-sdk/util-format-url": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.821.0.tgz", - "integrity": "sha512-h+xqmPToxDrZ0a7rxE1a8Oh4zpWfZe9oiQUphGtfiGFA6j75UiURH5J3MmGHa/G4t15I3iLLbYtUXxvb1i7evg==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.840.0.tgz", + "integrity": "sha512-VB1PWyI1TQPiPvg4w7tgUGGQER1xxXPNUqfh3baxUSFi1Oh8wHrDnFywkxLm3NMmgDmnLnSZ5Q326qAoyqKLSg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", + "@aws-sdk/types": "3.840.0", "@smithy/querystring-builder": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -681,25 +683,25 @@ } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.821.0.tgz", - "integrity": "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.840.0.tgz", + "integrity": "sha512-JdyZM3EhhL4PqwFpttZu1afDpPJCCc3eyZOLi+srpX11LsGj6sThf47TYQN75HT1CarZ7cCdQHGzP2uy3/xHfQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.821.0", + "@aws-sdk/types": "3.840.0", "@smithy/types": "^4.3.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.835.0.tgz", - "integrity": "sha512-gY63QZ4W5w9JYHYuqvUxiVGpn7IbCt1ODPQB0ZZwGGr3WRmK+yyZxCtFjbYhEQDQLgTWpf8YgVxgQLv2ps0PJg==", + "version": "3.840.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.840.0.tgz", + "integrity": "sha512-Fy5JUEDQU1tPm2Yw/YqRYYc27W5+QD/J4mYvQvdWjUGZLB5q3eLFMGD35Uc28ZFoGMufPr4OCxK/bRfWROBRHQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/types": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.840.0", + "@aws-sdk/types": "3.840.0", "@smithy/node-config-provider": "^4.1.3", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -1184,9 +1186,9 @@ } }, "node_modules/@smithy/core": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.5.3.tgz", - "integrity": "sha512-xa5byV9fEguZNofCclv6v9ra0FYh5FATQW/da7FQUVTic94DfrN/NvmKZjrMyzbpqfot9ZjBaO8U1UeTbmSLuA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.6.0.tgz", + "integrity": "sha512-Pgvfb+TQ4wUNLyHzvgCP4aYZMh16y7GcfF59oirRHcgGgkH1e/s9C0nv/v3WP+Quymyr5je71HeFQCwh+44XLg==", "license": "Apache-2.0", "dependencies": { "@smithy/middleware-serde": "^4.0.8", @@ -1290,12 +1292,12 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.12.tgz", - "integrity": "sha512-Piy/9UOjh5FtEXhybjPwyOHcC/pGHFknl2Gc/q1YbEkngxY6eQwvBvZTNamXpyDAHCuP3h+lymcVcdyO3WdGqQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.13.tgz", + "integrity": "sha512-xg3EHV/Q5ZdAO5b0UiIMj3RIOCobuS40pBBODguUDVdko6YK6QIzCVRrHTogVuEKglBWqWenRnZ71iZnLL3ZAQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.5.3", + "@smithy/core": "^3.6.0", "@smithy/middleware-serde": "^4.0.8", "@smithy/node-config-provider": "^4.1.3", "@smithy/shared-ini-file-loader": "^4.0.4", @@ -1309,15 +1311,15 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.13.tgz", - "integrity": "sha512-5ILvPCJevTcGpl7wAvSV9HKbIGS2Wxz505d0b5dP9kmjBhsFm1SAsSLIteMn925hlxPUkOsjcjMyaEiQDr9s4w==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.14.tgz", + "integrity": "sha512-eoXaLlDGpKvdmvt+YBfRXE7HmIEtFF+DJCbTPwuLunP0YUnrydl+C4tS+vEM0+nyxXrX3PSUFqC+lP1+EHB1Tw==", "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^4.1.3", "@smithy/protocol-http": "^5.1.2", "@smithy/service-error-classification": "^4.0.6", - "@smithy/smithy-client": "^4.4.4", + "@smithy/smithy-client": "^4.4.5", "@smithy/types": "^4.3.1", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.6", @@ -1484,13 +1486,13 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.4.tgz", - "integrity": "sha512-38Ivn1VoArWi+wvJeW6rGl9lcuViYjmGfaZaBgOlFEyoQSIl2Rnr3uOWzwu3FE8NIvHflQVkwbveMQxBAEbd1A==", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.5.tgz", + "integrity": "sha512-+lynZjGuUFJaMdDYSTMnP/uPBBXXukVfrJlP+1U/Dp5SFTEI++w6NMga8DjOENxecOF71V9Z2DllaVDYRnGlkg==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.5.3", - "@smithy/middleware-endpoint": "^4.1.12", + "@smithy/core": "^3.6.0", + "@smithy/middleware-endpoint": "^4.1.13", "@smithy/middleware-stack": "^4.0.4", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", @@ -1591,13 +1593,13 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.0.20", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.20.tgz", - "integrity": "sha512-496BbDMx/8kQrvlhT0EsX7JM7yVpK7CACmG3LsqMX9RaJnF7M/OVlfbxoRceUp5o5S0HqBnV8/xGOX7MYCv2Gw==", + "version": "4.0.21", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.21.tgz", + "integrity": "sha512-wM0jhTytgXu3wzJoIqpbBAG5U6BwiubZ6QKzSbP7/VbmF1v96xlAbX2Am/mz0Zep0NLvLh84JT0tuZnk3wmYQA==", "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.0.4", - "@smithy/smithy-client": "^4.4.4", + "@smithy/smithy-client": "^4.4.5", "@smithy/types": "^4.3.1", "bowser": "^2.11.0", "tslib": "^2.6.2" @@ -1607,16 +1609,16 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.0.20", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.20.tgz", - "integrity": "sha512-QsGHToYvRCoMyJQr/bXLG7L+nXNxICpG5LI1lRL0wkdkvLIxP89r4O+LHLWI9UeLzylxJ7VPnsTR/ADJ+F71/w==", + "version": "4.0.21", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.21.tgz", + "integrity": "sha512-/F34zkoU0GzpUgLJydHY8Rxu9lBn8xQC/s/0M0U9lLBkYbA1htaAFjWYJzpzsbXPuri5D1H8gjp2jBum05qBrA==", "license": "Apache-2.0", "dependencies": { "@smithy/config-resolver": "^4.1.4", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/node-config-provider": "^4.1.3", "@smithy/property-provider": "^4.0.4", - "@smithy/smithy-client": "^4.4.4", + "@smithy/smithy-client": "^4.4.5", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, @@ -1722,9 +1724,9 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.5.tgz", - "integrity": "sha512-4QvC49HTteI1gfemu0I1syWovJgPvGn7CVUoN9ZFkdvr/cCFkrEL7qNCdx/2eICqDWEGnnr68oMdSIPCLAriSQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.6.tgz", + "integrity": "sha512-slcr1wdRbX7NFphXZOxtxRNA7hXAAtJAXJDE/wdoMAos27SIquVCKiSqfB6/28YzQ8FCsB5NKkhdM5gMADbqxg==", "license": "Apache-2.0", "dependencies": { "@smithy/abort-controller": "^4.0.4", @@ -1735,6 +1737,16 @@ "node": ">=18.0.0" } }, + "node_modules/@types/node": { + "version": "20.19.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.4.tgz", + "integrity": "sha512-OP+We5WV8Xnbuvw0zC2m4qfB/BJvjyCwtNjhHdJxV1639SGSKrLmJkc3fMnp2Qy8nJyHp8RO6umxELN/dS1/EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, "node_modules/@types/uuid": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", @@ -1837,6 +1849,27 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", diff --git a/private-ca/server/package.json b/private-ca/server/package.json index 4863605..1f2d12a 100644 --- a/private-ca/server/package.json +++ b/private-ca/server/package.json @@ -2,10 +2,11 @@ "name": "private-ca", "version": "0.0.1", "description": "", - "main": "index_lambda.js", + "type": "module", + "main": "dist/index_lambda.js", "scripts": { - "start": "node index_lambda.js", - "test": "echo \"Error: no test specified\" && exit 1", + "start": "node dist/index_lambda.js", + "type-check": "tsc --noEmit", "build": "node build.mjs" }, "author": "", @@ -16,6 +17,8 @@ "node-forge": "^1.3.1" }, "devDependencies": { + "@types/node": "^20.10.5", + "typescript": "^5.3.3", "esbuild": "^0.25.5" } } diff --git a/private-ca/server/secret-manager-utils.js b/private-ca/server/secret-manager-utils.js deleted file mode 100644 index 680d8b2..0000000 --- a/private-ca/server/secret-manager-utils.js +++ /dev/null @@ -1,14 +0,0 @@ -import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager"; - -export const getSecret = async (secretRegion, secretPrefix, accountId) => { - const client = new SecretsManagerClient({ region: secretRegion }); - try { - const command = new GetSecretValueCommand({ SecretId: `${secretPrefix}_${accountId}_secret` }); - const response = await client.send(command); - const secret = JSON.parse(response.SecretString); - return secret; - } catch (err) { - console.log(err); - return null; - } -} \ No newline at end of file diff --git a/private-ca/server/format-date.js b/private-ca/server/src/format-date.ts similarity index 84% rename from private-ca/server/format-date.js rename to private-ca/server/src/format-date.ts index d2bbd6d..0505020 100644 --- a/private-ca/server/format-date.js +++ b/private-ca/server/src/format-date.ts @@ -1,4 +1,4 @@ -const formatDate = (date) => { +const formatDate = (date: Date): string => { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); @@ -9,4 +9,4 @@ const formatDate = (date) => { return `${year}${month}${day}${hours}${minutes}${seconds}`; }; -export default formatDate; \ No newline at end of file +export default formatDate; \ No newline at end of file diff --git a/private-ca/server/generate-client-ssh-cert.js b/private-ca/server/src/generate-client-ssh-cert.ts similarity index 60% rename from private-ca/server/generate-client-ssh-cert.js rename to private-ca/server/src/generate-client-ssh-cert.ts index 4c3840a..e5848f3 100644 --- a/private-ca/server/generate-client-ssh-cert.js +++ b/private-ca/server/src/generate-client-ssh-cert.ts @@ -2,31 +2,35 @@ import fs from 'fs'; import child_process from 'child_process'; import util from 'util'; import formatDate from './format-date.js'; +import { CallerIdentityResponse, SecretData } from './types/index.js'; const exec = util.promisify(child_process.exec); -const validityInDays = process.env.validityInDays ?? 1; +const validityInDays = parseInt(process.env.validityInDays ?? '1', 10); 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) => { - +export const signClientSSHCertificate = async ( + callerIdentity: CallerIdentityResponse, + secret: SecretData, + certPubkey: string +): Promise => { const arn = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn; const match = arn.match(/\/([^/]+)$/); if (!match) { - throw new Error(`Invalid ARN format: ${arn}`); + throw new Error(`Invalid ARN format: ${arn}`); } const roleName = match[1]; const user_ca = Buffer.from(secret.user_ca, 'base64').toString('utf-8'); - certPubkey = Buffer.from(certPubkey, 'base64').toString('utf-8'); + const decodedCertPubkey = Buffer.from(certPubkey, 'base64').toString('utf-8'); fs.writeFileSync(caKeyPath, user_ca); - fs.writeFileSync(publicKeyPath, certPubkey); + fs.writeFileSync(publicKeyPath, decodedCertPubkey); - let { stdout, stderr } = await exec(`chmod 600 ${caKeyPath}`); - console.log('stdout:', stdout); - console.log('stderr:', stderr); + let result = await exec(`chmod 600 ${caKeyPath}`); + console.log('stdout:', result.stdout); + console.log('stderr:', result.stderr); const now = new Date(); const validFrom = formatDate(now); @@ -36,16 +40,13 @@ export const signClientSSHCertificate = async (callerIdentity, secret, certPubke const validityPeriod = `${validFrom}:${validTo}`; - ( - { stdout, stderr } = await exec( + result = await exec( `ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I client_${roleName} -n ${roleName} -V ${validityPeriod} ${publicKeyPath}` - ) ); - console.log('stdout:', stdout); - console.log('stderr:', stderr); + console.log('stdout:', result.stdout); + console.log('stderr:', result.stderr); const certificate = fs.readFileSync(certificatePath, 'utf8'); return certificate; - -}; \ No newline at end of file +}; \ No newline at end of file diff --git a/private-ca/server/generate-host-ssh-cert.js b/private-ca/server/src/generate-host-ssh-cert.ts similarity index 56% rename from private-ca/server/generate-host-ssh-cert.js rename to private-ca/server/src/generate-host-ssh-cert.ts index db7aff8..154a6f9 100644 --- a/private-ca/server/generate-host-ssh-cert.js +++ b/private-ca/server/src/generate-host-ssh-cert.ts @@ -3,16 +3,21 @@ import child_process from 'child_process'; import util from 'util'; import { getPublicIpAddress } from './get-public-ip-address.js'; import formatDate from './format-date.js'; +import { CallerIdentityResponse, SecretData } from './types/index.js'; const exec = util.promisify(child_process.exec); -const validityInDays = process.env.validityInDays ?? 1; - -export const signHostSSHCertificate = async (callerIdentity, secret, certPubkey, awsEC2Region) => { - +const validityInDays = parseInt(process.env.validityInDays ?? '1', 10); + +export const signHostSSHCertificate = async ( + callerIdentity: CallerIdentityResponse, + secret: SecretData, + certPubkey: string, + awsEC2Region: string +): Promise => { const arn = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn; const match = arn.match(/\/([^/]+)$/); if (!match) { - throw new Error(`Invalid ARN format: ${arn}`); + throw new Error(`Invalid ARN format: ${arn}`); } const instanceId = match[1]; @@ -23,13 +28,14 @@ export const signHostSSHCertificate = async (callerIdentity, secret, certPubkey, 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'); + const decodedCertPubkey = Buffer.from(certPubkey, 'base64').toString('utf-8'); + fs.writeFileSync(caKeyPath, host_ca); - fs.writeFileSync(publicKeyPath, certPubkey); + fs.writeFileSync(publicKeyPath, decodedCertPubkey); - let { stdout, stderr } = await exec(`chmod 600 ${caKeyPath}`); - console.log('stdout:', stdout); - console.log('stderr:', stderr); + let result = await exec(`chmod 600 ${caKeyPath}`); + console.log('stdout:', result.stdout); + console.log('stderr:', result.stderr); const now = new Date(); const validFrom = formatDate(now); @@ -39,15 +45,13 @@ export const signHostSSHCertificate = async (callerIdentity, secret, certPubkey, const validityPeriod = `${validFrom}:${validTo}`; - ( - { stdout, stderr } = await exec( - `ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I host_${instanceId} -h -n ${publicIp} -V ${validityPeriod} ${publicKeyPath}` - ) + result = await exec( + `ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I host_${instanceId} -h -n ${publicIp} -V ${validityPeriod} ${publicKeyPath}` ); - console.log('stdout:', stdout); - console.log('stderr:', stderr); + console.log('stdout:', result.stdout); + console.log('stderr:', result.stderr); const certificate = fs.readFileSync(certificatePath, 'utf8'); return certificate; -}; +}; \ No newline at end of file diff --git a/private-ca/server/src/get-caller-identity.ts b/private-ca/server/src/get-caller-identity.ts new file mode 100644 index 0000000..87aded7 --- /dev/null +++ b/private-ca/server/src/get-caller-identity.ts @@ -0,0 +1,53 @@ +import https from 'https'; +import { LambdaEvent, CallerIdentityResponse } from './types/index.js'; + +export const getCallerIdentity = (event: LambdaEvent): Promise => { + 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 = { + 'accept': 'application/json', + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-Amz-Date': auth.amzDate, + 'Authorization': auth.authorizationHeader, + 'X-Amz-Security-Token': auth.sessionToken, + 'Aud': 'FundwaveCA' + }; + + const options: https.RequestOptions = { + 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: string) => { + data += chunk; + }); + + res.on('end', () => { + try { + const parsedData = JSON.parse(data) as CallerIdentityResponse; + resolve(parsedData); + } catch (err) { + reject(new Error(`Failed to parse response: ${err}`)); + } + }); + }); + + req.on('error', (error: Error) => { + reject(new Error(`Request failed: ${error.message}`)); + }); + + req.write(payload); + req.end(); + }); +}; \ No newline at end of file diff --git a/private-ca/server/get-public-ip-address.js b/private-ca/server/src/get-public-ip-address.ts similarity index 67% rename from private-ca/server/get-public-ip-address.js rename to private-ca/server/src/get-public-ip-address.ts index a140fa9..0fc1551 100644 --- a/private-ca/server/get-public-ip-address.js +++ b/private-ca/server/src/get-public-ip-address.ts @@ -1,6 +1,9 @@ import { EC2Client, DescribeInstancesCommand } from "@aws-sdk/client-ec2"; -export const getPublicIpAddress = async (region, instanceId) => { +export const getPublicIpAddress = async ( + region: string, + instanceId: string +): Promise => { const client = new EC2Client({ region }); const command = new DescribeInstancesCommand({ @@ -16,8 +19,12 @@ export const getPublicIpAddress = async (region, instanceId) => { throw new Error(`Instance ${instanceId} not found`); } + if (!instance.PublicIpAddress) { + throw new Error(`Instance ${instanceId} does not have a public IP address`); + } + return instance.PublicIpAddress; } catch (error) { throw error; } -}; \ No newline at end of file +}; \ No newline at end of file diff --git a/private-ca/server/src/index_lambda.ts b/private-ca/server/src/index_lambda.ts new file mode 100644 index 0000000..54c6747 --- /dev/null +++ b/private-ca/server/src/index_lambda.ts @@ -0,0 +1,93 @@ +import { signHostSSHCertificate } from './generate-host-ssh-cert.js'; +import { signClientSSHCertificate } from './generate-client-ssh-cert.js'; +import { getCallerIdentity } from './get-caller-identity.js'; +import { getSecret } from './secret-manager-utils.js'; +import { + LambdaEvent, + LambdaResponse, +} from './types/index.js'; + +export const handler = async (event: any): Promise => { + try { + const parsedEvent: LambdaEvent = JSON.parse(event.body); + + if (!parsedEvent.certPubkey) { + return { + statusCode: 400, + body: JSON.stringify({ error: 'Missing certPubkey' }), + }; + } + + // auth + const callerIdentity = await getCallerIdentity(parsedEvent); + const accountId = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Account; + + // secret + const secret = await getSecret('privateCA', accountId); + if (!secret) { + return { + statusCode: 401, + body: JSON.stringify({ + error: 'This AWS account is not configured to use this service' + }), + }; + } + + // action + switch (parsedEvent.action) { + case "generateHostSSHCert": { + if (!parsedEvent.awsEC2Region) { + return { + statusCode: 400, + body: JSON.stringify({ error: 'Missing awsEC2Region' }), + }; + } + + const hostSSHCert = await signHostSSHCertificate( + callerIdentity, + secret, + parsedEvent.certPubkey, + parsedEvent.awsEC2Region + ); + + return { + statusCode: 200, + body: JSON.stringify({ + certificate: Buffer.from(hostSSHCert).toString('base64'), + 'user_ca.pub': secret['user_ca.pub'] + }) + }; + } + + case "generateClientSSHCert": { + const clientSSHCert = await signClientSSHCertificate( + callerIdentity, + secret, + parsedEvent.certPubkey + ); + + return { + statusCode: 200, + body: JSON.stringify({ + certificate: Buffer.from(clientSSHCert).toString('base64'), + 'host_ca.pub': secret['host_ca.pub'] + }) + }; + } + + default: + console.log("Invalid Action"); + return { + statusCode: 400, + body: JSON.stringify({ error: 'Invalid Action' }), + }; + } + } catch (err) { + console.error('Error in cert signing handler:', err); + const errorMessage = err instanceof Error ? err.message : 'Internal server error'; + return { + statusCode: 500, + body: JSON.stringify({ error: errorMessage }), + }; + } +}; \ No newline at end of file diff --git a/private-ca/server/src/secret-manager-utils.ts b/private-ca/server/src/secret-manager-utils.ts new file mode 100644 index 0000000..4be10b4 --- /dev/null +++ b/private-ca/server/src/secret-manager-utils.ts @@ -0,0 +1,25 @@ +import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager"; +import { SecretData } from './types/index.js'; + +export const getSecret = async ( + secretPrefix: string, + accountId: string +): Promise => { + const client = new SecretsManagerClient(); + try { + const command = new GetSecretValueCommand({ + SecretId: `${secretPrefix}_${accountId}_secret` + }); + const response = await client.send(command); + + if (!response.SecretString) { + return null; + } + + const secret = JSON.parse(response.SecretString) as SecretData; + return secret; + } catch (err) { + console.log(err); + return null; + } +}; \ No newline at end of file diff --git a/private-ca/server/src/types/index.ts b/private-ca/server/src/types/index.ts new file mode 100644 index 0000000..0cfe643 --- /dev/null +++ b/private-ca/server/src/types/index.ts @@ -0,0 +1,36 @@ +export interface LambdaEvent { + body: string; + certPubkey: string; + action: 'generateHostSSHCert' | 'generateClientSSHCert'; + awsEC2Region?: string; + awsSTSRegion: string; + auth: AuthCredentials; +} + +export interface AuthCredentials { + amzDate: string; + authorizationHeader: string; + sessionToken: string; +} + +export interface CallerIdentityResponse { + GetCallerIdentityResponse: { + GetCallerIdentityResult: { + Account: string; + Arn: string; + UserId: string; + }; + }; +} + +export interface SecretData { + host_ca: string; + 'host_ca.pub': string; + user_ca: string; + 'user_ca.pub': string; +} + +export interface LambdaResponse { + statusCode: number; + body: string; +} \ No newline at end of file diff --git a/private-ca/server/tsconfig.json b/private-ca/server/tsconfig.json new file mode 100644 index 0000000..3ce3daf --- /dev/null +++ b/private-ca/server/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "node", + "lib": ["ES2020"], + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "removeComments": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "exactOptionalPropertyTypes": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] +} \ No newline at end of file From f522461b2d57632431918e801037eae83bfe1275 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Thu, 3 Jul 2025 15:34:34 +0530 Subject: [PATCH 099/146] docs: update README to remove note about AWS_SECRETS_REGION env variable --- private-ca/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/private-ca/README.md b/private-ca/README.md index d53e812..ce58b44 100644 --- a/private-ca/README.md +++ b/private-ca/README.md @@ -18,8 +18,6 @@ This creates the following resources on AWS: - 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_SECRETS_REGION` to store the region in which AWS secrets for privateCA reside. - ## Prerequisites for usage ### Running via Docker (for host machines only) From a6dfa8bc0aa7e1b188e9fabb97763027e9be2f98 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Thu, 3 Jul 2025 15:48:16 +0530 Subject: [PATCH 100/146] refactor: remove secretPrefix param from getSecret function --- private-ca/server/src/index_lambda.ts | 2 +- private-ca/server/src/secret-manager-utils.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/private-ca/server/src/index_lambda.ts b/private-ca/server/src/index_lambda.ts index 54c6747..39db79c 100644 --- a/private-ca/server/src/index_lambda.ts +++ b/private-ca/server/src/index_lambda.ts @@ -23,7 +23,7 @@ export const handler = async (event: any): Promise => { const accountId = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Account; // secret - const secret = await getSecret('privateCA', accountId); + const secret = await getSecret(accountId); if (!secret) { return { statusCode: 401, diff --git a/private-ca/server/src/secret-manager-utils.ts b/private-ca/server/src/secret-manager-utils.ts index 4be10b4..2fc7a3d 100644 --- a/private-ca/server/src/secret-manager-utils.ts +++ b/private-ca/server/src/secret-manager-utils.ts @@ -2,13 +2,12 @@ import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-sec import { SecretData } from './types/index.js'; export const getSecret = async ( - secretPrefix: string, accountId: string ): Promise => { const client = new SecretsManagerClient(); try { const command = new GetSecretValueCommand({ - SecretId: `${secretPrefix}_${accountId}_secret` + SecretId: `privateCA_${accountId}_secret` }); const response = await client.send(command); From eeeaa4b605821f37b597bcd4ef75d9a2d62e3120 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Thu, 3 Jul 2025 16:16:22 +0530 Subject: [PATCH 101/146] fix: update AWS_REGION and AWS_PROFILE defaults in deployment script --- private-ca/deploy-server-on-lambda.sh | 4 ++-- private-ca/server/src/secret-manager-utils.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/private-ca/deploy-server-on-lambda.sh b/private-ca/deploy-server-on-lambda.sh index 903e8bc..fe0e85d 100755 --- a/private-ca/deploy-server-on-lambda.sh +++ b/private-ca/deploy-server-on-lambda.sh @@ -6,8 +6,8 @@ ROLE_NAME=${2:-"privateCALambdaRole"} POLICY_NAME=${3:-"PrivateCAPolicy"} LAYER_NAME=${4:-"openssh"} FUNCTION_NAME=${5:-"privateCA"} -AWS_REGION=${6:-"ap-southeast-12"} -AWS_PROFILE=${7:-"harshit-root"} +AWS_REGION=${6:-"ap-southeast-1"} +AWS_PROFILE=${7:-"default"} ################## Secret ################## ACCOUNT_ID=$(aws sts get-caller-identity --profile $AWS_PROFILE | jq -r ".Account") diff --git a/private-ca/server/src/secret-manager-utils.ts b/private-ca/server/src/secret-manager-utils.ts index 2fc7a3d..e21a322 100644 --- a/private-ca/server/src/secret-manager-utils.ts +++ b/private-ca/server/src/secret-manager-utils.ts @@ -21,4 +21,4 @@ export const getSecret = async ( console.log(err); return null; } -}; \ No newline at end of file +}; \ No newline at end of file From 684dcdb4f9f2c2e7afeb92b41dcfd54c4a5aea07 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Mon, 7 Jul 2025 14:47:12 +0530 Subject: [PATCH 102/146] feat: add getHostCAPublicKey action in private-ca lambda --- private-ca/server/src/index_lambda.ts | 26 +++++++++++++++++++------- private-ca/server/src/types/index.ts | 2 +- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/private-ca/server/src/index_lambda.ts b/private-ca/server/src/index_lambda.ts index 39db79c..b9cef60 100644 --- a/private-ca/server/src/index_lambda.ts +++ b/private-ca/server/src/index_lambda.ts @@ -11,13 +11,6 @@ export const handler = async (event: any): Promise => { try { const parsedEvent: LambdaEvent = JSON.parse(event.body); - if (!parsedEvent.certPubkey) { - return { - statusCode: 400, - body: JSON.stringify({ error: 'Missing certPubkey' }), - }; - } - // auth const callerIdentity = await getCallerIdentity(parsedEvent); const accountId = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Account; @@ -36,6 +29,12 @@ export const handler = async (event: any): Promise => { // action switch (parsedEvent.action) { case "generateHostSSHCert": { + if (!parsedEvent.certPubkey) { + return { + statusCode: 400, + body: JSON.stringify({ error: 'Missing certPubkey' }), + }; + } if (!parsedEvent.awsEC2Region) { return { statusCode: 400, @@ -60,6 +59,12 @@ export const handler = async (event: any): Promise => { } case "generateClientSSHCert": { + if (!parsedEvent.certPubkey) { + return { + statusCode: 400, + body: JSON.stringify({ error: 'Missing certPubkey' }), + }; + } const clientSSHCert = await signClientSSHCertificate( callerIdentity, secret, @@ -74,6 +79,13 @@ export const handler = async (event: any): Promise => { }) }; } + + case "getHostCAPublicKey": { + return { + statusCode: 200, + body: JSON.stringify({ 'host_ca.pub': secret['host_ca.pub'] }) + }; + } default: console.log("Invalid Action"); diff --git a/private-ca/server/src/types/index.ts b/private-ca/server/src/types/index.ts index 0cfe643..9e9c48e 100644 --- a/private-ca/server/src/types/index.ts +++ b/private-ca/server/src/types/index.ts @@ -1,7 +1,7 @@ export interface LambdaEvent { body: string; certPubkey: string; - action: 'generateHostSSHCert' | 'generateClientSSHCert'; + action: 'generateHostSSHCert' | 'generateClientSSHCert' | 'getHostCAPublicKey'; awsEC2Region?: string; awsSTSRegion: string; auth: AuthCredentials; From 9947763c8ae006e5d32ab6bbaffe79cb4a45ac03 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 8 Jul 2025 14:43:04 +0530 Subject: [PATCH 103/146] feat: add script to retrieve host CA public key and update known_hosts file --- private-ca/client/get-host-ca-public-key.sh | 97 +++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 private-ca/client/get-host-ca-public-key.sh diff --git a/private-ca/client/get-host-ca-public-key.sh b/private-ca/client/get-host-ca-public-key.sh new file mode 100644 index 0000000..35836dc --- /dev/null +++ b/private-ca/client/get-host-ca-public-key.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +CA_URL=${1:-$CA_URL} +AWS_PROFILE=${2:-"default"} +USER_SSH_DIR=${3:-"/home/$USER/.ssh"} +USER_AWS_DIR=${4:-"/home/$USER/.aws"} +AWS_STS_REGION=${5:-"ap-southeast-1"} + +PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) +[[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } + + +is_mfa_enabled() { + grep -q 'get-credentials' ${USER_AWS_DIR}/credentials +} + +get_aws_credentials() { + local TEMP_CREDS + + if is_mfa_enabled; then + CALLER_IDENTITY=$(aws sts get-caller-identity --profile $AWS_PROFILE) + [[ $? -ne 0 ]] && { echo "Your AWS credentials have either expired or are invalid. Please check your credentials and try again."; exit 1; } + + TEMP_CREDS=$(get-credentials $AWS_PROFILE) + else + #check if AWS creds are exposed as env variables, including AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN + if [[ -n "$AWS_ACCESS_KEY_ID" && -n "$AWS_SECRET_ACCESS_KEY" && -n "$AWS_SESSION_TOKEN" ]]; then + CALLER_IDENTITY=$(aws sts get-caller-identity) + [[ $? -ne 0 ]] && { echo "Your AWS credentials have either expired or are invalid. Please check your credentials and try again."; exit 1; } + TEMP_CREDS=$(echo "{\"AccessKeyId\":\"$AWS_ACCESS_KEY_ID\",\"SecretAccessKey\":\"$AWS_SECRET_ACCESS_KEY\",\"Token\":\"$AWS_SESSION_TOKEN\"}") + else + TEMP_CREDS=$(aws sts get-session-token --profile $AWS_PROFILE | jq -r ".Credentials") + fi + fi + + 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 // .SessionToken // .Sessiontoken") + echo "Using AWS credentials from environment variables $AWS_ACCESS_KEY_ID, $AWS_SECRET_ACCESS_KEY, $AWS_SESSION_TOKEN" +} + +get_aws_credentials $ENVIRONMENT + +if [ ! -d "private-ca-client-env" ]; then + $PYTHON_EXEC -m venv private-ca-client-env +fi +source private-ca-client-env/bin/activate +pip install -q --upgrade boto3 + +# Update PYTHON_EXEC to use the Python executable from the activated virtual environment +# This ensures we use the venv's Python with the installed dependencies (boto3) +PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) + +# Auth Headers +output=$($PYTHON_EXEC aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_STS_REGION) || { + echo "Failed to generate auth header. Check aws-auth-header.py script."; + exit 1; +} +auth_header=$(echo "$output" | jq -er ".Authorization") || { + echo "Failed to parse Authorization from auth-header output."; + exit 1; +} +date=$(echo "$output" | jq -er ".Date") || { + echo "Failed to parse Date from auth-header output."; + exit 1; +} + +EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"}, \"action\":\"getHostCAPublicKey\", \"awsSTSRegion\":\"${AWS_STS_REGION}\"}") + +read -r STATUS_CODE LAMBDA_RESPONSE < <( + curl -s "${CA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" -w "%{http_code}\n" | + { + response=$(cat) + status_code=${response: -3} + body=${response:0:$((${#response}-3))} + echo "$status_code $body" + } +) + +if [[ "$STATUS_CODE" != "200" ]]; then + echo "CA request failed (Status: ${STATUS_CODE}): ${LAMBDA_RESPONSE}" + exit 1; +fi + +HOST_CA_PUBKEY=$(echo $LAMBDA_RESPONSE | jq -r ".\"host_ca.pub\"" | base64 -d) + +[[ -f "${USER_SSH_DIR}/known_hosts" ]] || touch "${USER_SSH_DIR}/known_hosts" + +# Add host CA public key to known_hosts file if it doesn't exist +if [[ $(grep -q "@cert-authority" "${USER_SSH_DIR}/known_hosts"; echo $?) -ne 0 ]]; then + # @cert-authority tells ssh to trust the host CA public key + # * means all hosts (wildcard) (you can also specify a list of comma separated hostnames) + # ${HOST_CA_PUBKEY} is the host CA public key that was used to sign the host certificate + echo "@cert-authority * ${HOST_CA_PUBKEY}" >> ${USER_SSH_DIR}/known_hosts +fi + +deactivate \ No newline at end of file From e371e4b992f46f2bc484e209178453f7c8decab6 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 8 Jul 2025 16:52:02 +0530 Subject: [PATCH 104/146] chore: remove debug echo statement for AWS credentials in get-host-ca-public-key.sh --- private-ca/client/get-host-ca-public-key.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/private-ca/client/get-host-ca-public-key.sh b/private-ca/client/get-host-ca-public-key.sh index 35836dc..3188508 100644 --- a/private-ca/client/get-host-ca-public-key.sh +++ b/private-ca/client/get-host-ca-public-key.sh @@ -36,7 +36,6 @@ get_aws_credentials() { 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 // .SessionToken // .Sessiontoken") - echo "Using AWS credentials from environment variables $AWS_ACCESS_KEY_ID, $AWS_SECRET_ACCESS_KEY, $AWS_SESSION_TOKEN" } get_aws_credentials $ENVIRONMENT From c98e6d30ec8e7e6900ac3b3b3785925212f0b692 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 9 Jul 2025 12:02:23 +0530 Subject: [PATCH 105/146] chore: disable pip version check when upgrading boto3 in generate-certificate-curl.sh --- private-ca/client/generate-certificate-curl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 8020654..dac2239 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -130,7 +130,7 @@ if [ ! -d "private-ca-client-env" ]; then $PYTHON_EXEC -m venv private-ca-client-env fi source private-ca-client-env/bin/activate -pip install -q --upgrade boto3 +pip install -q --upgrade --disable-pip-version-check boto3 # Update PYTHON_EXEC to use the Python executable from the activated virtual environment # This ensures we use the venv's Python with the installed dependencies (boto3) From 832a4c772119dd0c883ef8efa521d622a65b0682 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 9 Jul 2025 12:18:56 +0530 Subject: [PATCH 106/146] chore: disable pip version check in certificate generation scripts --- private-ca/client/generate-certificate-aws-cli.sh | 2 +- private-ca/client/get-host-ca-public-key.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index 18ef124..2e090d7 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -144,7 +144,7 @@ if [ ! -d "private-ca-client-env" ]; then $PYTHON_EXEC -m venv private-ca-client-env fi source private-ca-client-env/bin/activate -pip install -q --upgrade boto3 +pip install -q --upgrade --disable-pip-version-check boto3 # Update PYTHON_EXEC to use the Python executable from the activated virtual environment # This ensures we use the venv's Python with the installed dependencies (boto3) diff --git a/private-ca/client/get-host-ca-public-key.sh b/private-ca/client/get-host-ca-public-key.sh index 3188508..df46473 100644 --- a/private-ca/client/get-host-ca-public-key.sh +++ b/private-ca/client/get-host-ca-public-key.sh @@ -44,7 +44,7 @@ if [ ! -d "private-ca-client-env" ]; then $PYTHON_EXEC -m venv private-ca-client-env fi source private-ca-client-env/bin/activate -pip install -q --upgrade boto3 +pip install -q --upgrade --disable-pip-version-check boto3 # Update PYTHON_EXEC to use the Python executable from the activated virtual environment # This ensures we use the venv's Python with the installed dependencies (boto3) From 4f5461e1617e7607ba2c6e28d4926753c3d866f8 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 9 Jul 2025 12:27:44 +0530 Subject: [PATCH 107/146] make user SSH and AWS directory paths cross-platform by using $HOME --- private-ca/client/generate-certificate-aws-cli.sh | 4 ++-- private-ca/client/generate-certificate-curl.sh | 4 ++-- private-ca/client/get-host-ca-public-key.sh | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh index 2e090d7..65f9709 100755 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -5,8 +5,8 @@ trap 'rm -f *.json' EXIT CA_ACTION=${1:-"generateHostSSHCert"} ENVIRONMENT=${2:-"client"} AWS_PROFILE=${3:-"default"} -USER_SSH_DIR=${4:-"/home/$USER/.ssh"} -USER_AWS_DIR=${5:-"/home/$USER/.aws"} +USER_SSH_DIR=${4:-"$HOME/.ssh"} +USER_AWS_DIR=${5:-"$HOME/.aws"} SYSTEM_SSH_DIR=${6:-"/etc/ssh"} LAMBDA_REGION=${7:-'us-west-2'} CA_LAMBDA_FUNCTION_NAME=${8:-"privateCA"} diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index dac2239..021f74f 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -4,8 +4,8 @@ CA_ACTION=${1:-$CA_ACTION} CA_URL=${2:-$CA_URL} ENVIRONMENT=${3:-"client"} AWS_PROFILE=${4:-"default"} -USER_SSH_DIR=${5:-"/home/$USER/.ssh"} -USER_AWS_DIR=${6:-"/home/$USER/.aws"} +USER_SSH_DIR=${5:-"$HOME/.ssh"} +USER_AWS_DIR=${6:-"$HOME/.aws"} SYSTEM_SSH_DIR=${7:-"/etc/ssh"} AWS_STS_REGION=${8:-"ap-southeast-1"} AWS_EC2_REGION=${9:-"us-west-2"} diff --git a/private-ca/client/get-host-ca-public-key.sh b/private-ca/client/get-host-ca-public-key.sh index df46473..b46f1a5 100644 --- a/private-ca/client/get-host-ca-public-key.sh +++ b/private-ca/client/get-host-ca-public-key.sh @@ -2,8 +2,8 @@ CA_URL=${1:-$CA_URL} AWS_PROFILE=${2:-"default"} -USER_SSH_DIR=${3:-"/home/$USER/.ssh"} -USER_AWS_DIR=${4:-"/home/$USER/.aws"} +USER_SSH_DIR=${3:-"$HOME/.ssh"} +USER_AWS_DIR=${4:-"$HOME/.aws"} AWS_STS_REGION=${5:-"ap-southeast-1"} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) From a2a448d3d452a20bf3af1bf5ff25c9529fa2cdb3 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 9 Jul 2025 17:19:41 +0530 Subject: [PATCH 108/146] feat: remove validity check for host cert, add cleanup function for error conditions, restart SSHD after changing config - New host cert is generated even if the existing cert was valid - Adds clean_config_on_error function to clean up partial SSH config on failure - Automatically restarts sshd after modifying sshd_config --- .../client/generate-certificate-curl.sh | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 021f74f..94b3507 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -13,6 +13,22 @@ AWS_EC2_REGION=${9:-"us-west-2"} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) [[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } +trap 'clean_config_on_error' EXIT + +clean_config_on_error() { + [ $? = 0 ] && success="1" || success="0" + if [[ $success = "0" && $CA_ACTION = "generateHostSSHCert" ]]; then + [[ "$(uname)" == "Darwin" ]] && SED_INPLACE="sed -i ''" || SED_INPLACE="sed -i" + + ${SED_INPLACE} "\|^HostCertificate ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config + ${SED_INPLACE} "\|^TrustedUserCAKeys ${SYSTEM_SSH_DIR}/user_ca.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config + + rm ${SYSTEM_SSH_DIR}/user_ca.pub + rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub + systemctl restart sshd + fi +} + is_mfa_enabled() { grep -q 'get-credentials' ${USER_AWS_DIR}/credentials } @@ -78,14 +94,16 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then 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 "" + test -f ${USER_SSH_DIR}/id_rsa.pub || { + ssh-keygen -t rsa -b 4096 -f ${USER_SSH_DIR}/id_rsa -C host_ca -N "" + rm ${USER_SSH_DIR}/id_rsa-cert.pub + } CERT_PUBKEY=$(cat ${USER_SSH_DIR}/id_rsa.pub | base64 | tr -d \\n) elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then @@ -99,21 +117,7 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then exit 1 fi - if test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub; then - # Host SSH Certificate already exists - current_timestamp=$(TZ=UTC date -u +"%Y-%m-%dT%H:%M:%S") - certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub | awk '/Valid:/{print $NF}') - - 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 - rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub - fi - fi + test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub && rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub 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 | tr -d \\n) else @@ -228,6 +232,7 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then 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 + systemctl restart sshd fi deactivate \ No newline at end of file From ae2a2ea8963e997e6709cd2349a208bb51f89a8d Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 9 Jul 2025 17:49:12 +0530 Subject: [PATCH 109/146] refactor generate-certificate-curl.sh/clean_config_on_error function --- private-ca/client/generate-certificate-curl.sh | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 94b3507..31dff30 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -16,16 +16,15 @@ PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) trap 'clean_config_on_error' EXIT clean_config_on_error() { - [ $? = 0 ] && success="1" || success="0" - if [[ $success = "0" && $CA_ACTION = "generateHostSSHCert" ]]; then - [[ "$(uname)" == "Darwin" ]] && SED_INPLACE="sed -i ''" || SED_INPLACE="sed -i" - - ${SED_INPLACE} "\|^HostCertificate ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config - ${SED_INPLACE} "\|^TrustedUserCAKeys ${SYSTEM_SSH_DIR}/user_ca.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config + if [[ $? -ne 0 && $CA_ACTION = "generateHostSSHCert" ]]; then + echo "Error occurred. Cleaning host SSH config..." + sed -i "\|^HostCertificate ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config + sed -i "\|^TrustedUserCAKeys ${SYSTEM_SSH_DIR}/user_ca.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config rm ${SYSTEM_SSH_DIR}/user_ca.pub rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub systemctl restart sshd + echo "Host SSH config cleaned." fi } From caca1150566e7a9b76e9dcdfb0eb4822087acb48 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 9 Jul 2025 18:21:24 +0530 Subject: [PATCH 110/146] feat (get-host-ca-public-key): early return in case of existing Host CA entry in known_hosts --- private-ca/client/get-host-ca-public-key.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/private-ca/client/get-host-ca-public-key.sh b/private-ca/client/get-host-ca-public-key.sh index b46f1a5..20ba2e9 100644 --- a/private-ca/client/get-host-ca-public-key.sh +++ b/private-ca/client/get-host-ca-public-key.sh @@ -9,6 +9,10 @@ AWS_STS_REGION=${5:-"ap-southeast-1"} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) [[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } +if grep -q "^@cert-authority" "${USER_SSH_DIR}/known_hosts"; then + echo "Host CA entry already present in known_hosts. Exiting." + exit 0 +fi is_mfa_enabled() { grep -q 'get-credentials' ${USER_AWS_DIR}/credentials From 0ffda0fe30f29382ddc4059a112af7bdfe1654c2 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Thu, 10 Jul 2025 11:06:25 +0530 Subject: [PATCH 111/146] feat (generate-certificate-curl.sh): Add check for AWS credentials as environment variables --- private-ca/client/generate-certificate-curl.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 31dff30..40c3a19 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -54,7 +54,14 @@ get_aws_credentials() { TEMP_CREDS=$(get-credentials $AWS_PROFILE) else - TEMP_CREDS=$(aws sts get-session-token --profile $AWS_PROFILE | jq -r ".Credentials") + #check if AWS creds are exposed as env variables (for example, in GitHub Actions), including AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN + if [[ -n "$AWS_ACCESS_KEY_ID" && -n "$AWS_SECRET_ACCESS_KEY" && -n "$AWS_SESSION_TOKEN" ]]; then + CALLER_IDENTITY=$(aws sts get-caller-identity) + [[ $? -ne 0 ]] && { echo "Your AWS credentials have either expired or are invalid. Please check your credentials and try again."; exit 1; } + TEMP_CREDS=$(echo "{\"AccessKeyId\":\"$AWS_ACCESS_KEY_ID\",\"SecretAccessKey\":\"$AWS_SECRET_ACCESS_KEY\",\"Token\":\"$AWS_SESSION_TOKEN\"}") + else + TEMP_CREDS=$(aws sts get-session-token --profile $AWS_PROFILE | jq -r ".Credentials") + fi fi else echo "Invalid environment provided. Allowed values are 'host' and 'client'"; exit 1; From 353283864c98fce02c671f160cc0e714f29ce3e6 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Thu, 10 Jul 2025 13:33:42 +0530 Subject: [PATCH 112/146] refactor (generate-certificate-curl.sh): update AWS credentials retrieval process, remove MFA check and get-credentials util dependency --- .../client/generate-certificate-curl.sh | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 40c3a19..0b7f97d 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -28,10 +28,6 @@ clean_config_on_error() { fi } -is_mfa_enabled() { - grep -q 'get-credentials' ${USER_AWS_DIR}/credentials -} - get_aws_credentials() { local method=${1:-"host"} local TEMP_CREDS @@ -48,20 +44,14 @@ get_aws_credentials() { TEMP_CREDS=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$INSTANCE_ROLE_NAME) elif [[ $method == "client" ]]; then - if is_mfa_enabled; then - CALLER_IDENTITY=$(aws sts get-caller-identity --profile $AWS_PROFILE) + if [[ -n "$AWS_ACCESS_KEY_ID" && -n "$AWS_SECRET_ACCESS_KEY" && -n "$AWS_SESSION_TOKEN" ]]; then + CALLER_IDENTITY=$(aws sts get-caller-identity) [[ $? -ne 0 ]] && { echo "Your AWS credentials have either expired or are invalid. Please check your credentials and try again."; exit 1; } - - TEMP_CREDS=$(get-credentials $AWS_PROFILE) + TEMP_CREDS=$(echo "{\"AccessKeyId\":\"$AWS_ACCESS_KEY_ID\",\"SecretAccessKey\":\"$AWS_SECRET_ACCESS_KEY\",\"Token\":\"$AWS_SESSION_TOKEN\"}") else - #check if AWS creds are exposed as env variables (for example, in GitHub Actions), including AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN - if [[ -n "$AWS_ACCESS_KEY_ID" && -n "$AWS_SECRET_ACCESS_KEY" && -n "$AWS_SESSION_TOKEN" ]]; then - CALLER_IDENTITY=$(aws sts get-caller-identity) - [[ $? -ne 0 ]] && { echo "Your AWS credentials have either expired or are invalid. Please check your credentials and try again."; exit 1; } - TEMP_CREDS=$(echo "{\"AccessKeyId\":\"$AWS_ACCESS_KEY_ID\",\"SecretAccessKey\":\"$AWS_SECRET_ACCESS_KEY\",\"Token\":\"$AWS_SESSION_TOKEN\"}") - else - TEMP_CREDS=$(aws sts get-session-token --profile $AWS_PROFILE | jq -r ".Credentials") - fi + TEMP_CREDS=$(aws configure export-credentials --profile $AWS_PROFILE 2>/dev/null) + SESSION_TOKEN=$(echo "$TEMP_CREDS" | jq -r '.Token // .SessionToken // .Sessiontoken // empty') + [[ -z "$SESSION_TOKEN" ]] && TEMP_CREDS=$(aws sts get-session-token --profile $AWS_PROFILE | jq -r ".Credentials") fi else echo "Invalid environment provided. Allowed values are 'host' and 'client'"; exit 1; From 521037b5d91c6f75e87e3dfe6ea189aa555d8ec3 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Thu, 17 Jul 2025 13:42:07 +0530 Subject: [PATCH 113/146] validity check for host certs, safer way to replace old cert with new one, more echo msgs --- .../client/generate-certificate-curl.sh | 54 ++++++++++++++++--- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 0b7f97d..9ff23a0 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -16,7 +16,7 @@ PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) trap 'clean_config_on_error' EXIT clean_config_on_error() { - if [[ $? -ne 0 && $CA_ACTION = "generateHostSSHCert" ]]; then + if [[ $? -ne 0 && $CA_ACTION == "generateHostSSHCert" && $CERT_VALID == "false" ]]; then echo "Error occurred. Cleaning host SSH config..." sed -i "\|^HostCertificate ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config sed -i "\|^TrustedUserCAKeys ${SYSTEM_SSH_DIR}/user_ca.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config @@ -113,7 +113,21 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then exit 1 fi - test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub && rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub + # Check if a valid certificate exists + CERT_VALID=false + if test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub; then + current_timestamp=$(TZ=UTC date -u +"%Y-%m-%dT%H:%M:%S") + certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub 2>/dev/null | awk '/Valid:/{print $NF}') + + if [[ $certificate_expiration_timestamp > $current_timestamp ]]; then + CERT_VALID=true + echo "A valid certificate was found at ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub." + else + echo "Existing certificate is expired or invalid." + rm -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub + 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 | tr -d \\n) else @@ -202,20 +216,48 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then if [[ "$STATUS_CODE" != "200" ]]; then echo "CA request failed (Status: ${STATUS_CODE}): ${LAMBDA_RESPONSE}" + if [[ "$CERT_VALID" == "true" ]]; then + echo "Keeping existing valid certificate." + fi exit 1; fi + ENCODED_CERTIFICATE=$(echo "$LAMBDA_RESPONSE" | jq -er ".certificate") || { echo "Certificate not found in Lambda response. Aborting."; + if [[ "$CERT_VALID" == "true" ]]; then + echo "Keeping existing valid certificate." + fi exit 1; } + CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) USER_CA_PUBKEY=$(echo $LAMBDA_RESPONSE | jq -r ".\"user_ca.pub\"" | base64 -d) - if [[ -n "$CERTIFICATE" ]]; then - echo "$CERTIFICATE" > "${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" - echo "Certificate written to ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" - else + if [[ -z "$CERTIFICATE" ]]; then echo "Empty certificate received. Not writing to disk." + if [[ "$CERT_VALID" == "true" ]]; then + echo "Keeping existing valid certificate." + fi + exit 1 + fi + + # Write new certificate to temporary file first + TEMP_CERT_FILE="${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub.tmp" + echo "$CERTIFICATE" > "$TEMP_CERT_FILE" + + # Verify the new certificate is valid + if ssh-keygen -Lf "$TEMP_CERT_FILE" >/dev/null 2>&1; then + # New certificate is valid, replace the old one + mv "$TEMP_CERT_FILE" "${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" + echo "New certificate written to ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" + CERT_VALID=true + else + # New certificate is invalid + rm -f "$TEMP_CERT_FILE" + echo "Generated certificate is invalid. Discarding." + if [[ "$CERT_VALID" == "true" ]]; then + echo "Keeping existing valid certificate." + fi exit 1 fi From 4fa21422cae8f3505be3eb0186e2d68bdb83a9d1 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Thu, 17 Jul 2025 13:49:03 +0530 Subject: [PATCH 114/146] safe replacement of old user certs with new ones --- .../client/generate-certificate-curl.sh | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 9ff23a0..4f89f89 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -82,18 +82,22 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then [[ -d "${USER_SSH_DIR}" ]] || { echo "User SSH directory does not exist. Please provide the correct user SSH directory."; exit 1; } + # Check if a valid certificate exists + CERT_VALID=false if test -f ${USER_SSH_DIR}/id_rsa-cert.pub; then # Client SSH Certificate already exists current_timestamp=$(TZ=UTC date -u +"%Y-%m-%dT%H:%M:%S") - certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${USER_SSH_DIR}/id_rsa-cert.pub | awk '/Valid:/{print $NF}') + certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${USER_SSH_DIR}/id_rsa-cert.pub 2>/dev/null | awk '/Valid:/{print $NF}') if [[ $certificate_expiration_timestamp > $current_timestamp ]]; then # Certificate is valid + CERT_VALID=true echo "A valid certificate was found at ${USER_SSH_DIR}/id_rsa-cert.pub." exit; else # Certificate expired - rm ${USER_SSH_DIR}/id_rsa-cert.pub + echo "Existing certificate is expired or invalid." + rm -f ${USER_SSH_DIR}/id_rsa-cert.pub fi fi test -f ${USER_SSH_DIR}/id_rsa.pub || { @@ -180,17 +184,50 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then if [[ "$STATUS_CODE" != "200" ]]; then echo "CA request failed (Status: ${STATUS_CODE}): ${LAMBDA_RESPONSE}" + if [[ "$CERT_VALID" == "true" ]]; then + echo "Keeping existing valid certificate." + fi exit 1; fi + ENCODED_CERTIFICATE=$(echo "$LAMBDA_RESPONSE" | jq -er ".certificate") || { echo "Certificate not found in Lambda response. Aborting."; + if [[ "$CERT_VALID" == "true" ]]; then + echo "Keeping existing valid certificate." + fi exit 1; } + 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 [[ -z "$CERTIFICATE" ]]; then + echo "Empty certificate received. Not writing to disk." + if [[ "$CERT_VALID" == "true" ]]; then + echo "Keeping existing valid certificate." + fi + exit 1 + fi + + # Write new certificate to temporary file first + TEMP_CERT_FILE="${USER_SSH_DIR}/id_rsa-cert.pub.tmp" + echo "$CERTIFICATE" > "$TEMP_CERT_FILE" + + # Verify the new certificate is valid + if ssh-keygen -Lf "$TEMP_CERT_FILE" >/dev/null 2>&1; then + # New certificate is valid, replace the old one + mv "$TEMP_CERT_FILE" "${USER_SSH_DIR}/id_rsa-cert.pub" + echo "New certificate written to ${USER_SSH_DIR}/id_rsa-cert.pub" + CERT_VALID=true + else + # New certificate is invalid + rm -f "$TEMP_CERT_FILE" + echo "Generated certificate is invalid. Discarding." + if [[ "$CERT_VALID" == "true" ]]; then + echo "Keeping existing valid certificate." + fi + exit 1 + fi [[ -f "${USER_SSH_DIR}/known_hosts" ]] || touch "${USER_SSH_DIR}/known_hosts" From d2c898785479132d59fd52654e2600511406e333 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Thu, 17 Jul 2025 17:55:55 +0530 Subject: [PATCH 115/146] add support for multiple CAs on both host and user side - handling CA key rotation - change identifier for certs - replace the Public keys stored on client sides each time cert is generated --- .../client/generate-certificate-curl.sh | 25 ++++++++++++++----- private-ca/deploy-server-on-lambda.sh | 4 +-- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 4f89f89..0821043 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -231,12 +231,16 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then [[ -f "${USER_SSH_DIR}/known_hosts" ]] || touch "${USER_SSH_DIR}/known_hosts" - # Add host CA public key to known_hosts file if it doesn't exist - if [[ $(grep -q "@cert-authority" "${USER_SSH_DIR}/known_hosts"; echo $?) -ne 0 ]]; then - # @cert-authority tells ssh to trust the host CA public key + # Add host CA public key to known_hosts file if it doesn't exist and update it if it does + # @cert-authority tells ssh to trust the host CA public key + # ${HOST_CA_PUBKEY} is the host CA public key that was used to sign the host certificate + if grep -qE '^@cert-authority .* fundwave_host_ca$' "${USER_SSH_DIR}/known_hosts"; then + # Update existing line + sed -i.bak -E "s|^(@cert-authority .*) ssh-rsa .*|\1 ${HOST_CA_PUBKEY}|" "${USER_SSH_DIR}/known_hosts" + else + # Add new line # * means all hosts (wildcard) (you can also specify a list of comma separated hostnames) - # ${HOST_CA_PUBKEY} is the host CA public key that was used to sign the host certificate - echo "@cert-authority * ${HOST_CA_PUBKEY}" >> ${USER_SSH_DIR}/known_hosts + echo "@cert-authority * ${HOST_CA_PUBKEY}" >> "${USER_SSH_DIR}/known_hosts" fi # sudo access is required to generate host certificate @@ -298,7 +302,16 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then exit 1 fi - test -f ${SYSTEM_SSH_DIR}/user_ca.pub || echo $USER_CA_PUBKEY > ${SYSTEM_SSH_DIR}/user_ca.pub + [[ -f "${SYSTEM_SSH_DIR}/user_ca.pub" ]] || touch "${SYSTEM_SSH_DIR}/user_ca.pub" + + if grep -qE '.* fundwave_host_ca$' "${SYSTEM_SSH_DIR}/user_ca.pub"; then + # Update existing line + sed -i.bak -E "s|ssh-rsa .* fundwave_host_ca$|${USER_CA_PUBKEY}|" "${SYSTEM_SSH_DIR}/user_ca.pub" + else + # Add new line + # * means all hosts (wildcard) (you can also specify a list of comma separated hostnames) + echo "${USER_CA_PUBKEY}" >> "${SYSTEM_SSH_DIR}/user_ca.pub" + fi 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 diff --git a/private-ca/deploy-server-on-lambda.sh b/private-ca/deploy-server-on-lambda.sh index fe0e85d..0d19875 100755 --- a/private-ca/deploy-server-on-lambda.sh +++ b/private-ca/deploy-server-on-lambda.sh @@ -13,8 +13,8 @@ AWS_PROFILE=${7:-"default"} ACCOUNT_ID=$(aws sts get-caller-identity --profile $AWS_PROFILE | jq -r ".Account") # 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 "" +ssh-keygen -t rsa -b 4096 -f host_ca -C fundwave_host_ca -N "" +ssh-keygen -t rsa -b 4096 -f user_ca -C fundwave_user_ca -N "" HOST_CA_PRIVATE_KEY=$(cat host_ca | base64 | tr -d \\n) HOST_CA_PUBLIC_KEY=$(cat host_ca.pub | base64 | tr -d \\n) From 35808c6c8d150c0ded16fc169b1ae788be4555ee Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 18 Jul 2025 14:18:55 +0530 Subject: [PATCH 116/146] feat (generate-certificate-curl.sh): check for known_hosts entry alongside cert validity and regenerate cert if not found --- private-ca/client/generate-certificate-curl.sh | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 0821043..70b30d5 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -91,11 +91,17 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then if [[ $certificate_expiration_timestamp > $current_timestamp ]]; then # Certificate is valid - CERT_VALID=true - echo "A valid certificate was found at ${USER_SSH_DIR}/id_rsa-cert.pub." - exit; + if [[ -f "${USER_SSH_DIR}/known_hosts" ]]; then + if grep -qE '^@cert-authority .* fundwave_host_ca$' "${USER_SSH_DIR}/known_hosts"; then + CERT_VALID=true + echo "A valid certificate and known_hosts entry were found." + else + echo "Certificate is valid, but known_hosts entry is missing." + fi + else + echo "Certificate is valid, but known_hosts file is missing." + fi else - # Certificate expired echo "Existing certificate is expired or invalid." rm -f ${USER_SSH_DIR}/id_rsa-cert.pub fi From 42750e80268122b777bb26cd8d0dbffb04ffe0fd Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 22 Jul 2025 11:32:44 +0530 Subject: [PATCH 117/146] improve certificate validity check to consider next cron run timing --- private-ca/client/generate-certificate-curl.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 70b30d5..46ced5f 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -125,15 +125,18 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then # Check if a valid certificate exists CERT_VALID=false + half_life_seconds=302400 if test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub; then current_timestamp=$(TZ=UTC date -u +"%Y-%m-%dT%H:%M:%S") certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub 2>/dev/null | awk '/Valid:/{print $NF}') - - if [[ $certificate_expiration_timestamp > $current_timestamp ]]; then + [[ $(uname) == "Darwin" ]] && cert_expiry_epoch=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$certificate_expiration_timestamp" +"%s") || cert_expiry_epoch=$(date -d "$certificate_expiration_timestamp" +"%s") + next_run_timestamp=$((current_timestamp + half_life_seconds)) + + if [[ $cert_expiry_epoch -gt $next_run_timestamp ]]; then CERT_VALID=true echo "A valid certificate was found at ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub." else - echo "Existing certificate is expired or invalid." + echo "Existing certificate will expire before next cron run." rm -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub fi fi From e65abbcf4846a23fb2d916fdcf21bdaa2996394b Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Tue, 22 Jul 2025 13:46:55 +0530 Subject: [PATCH 118/146] update default validity period for host SSH certificates to 7 days --- private-ca/server/src/generate-host-ssh-cert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/private-ca/server/src/generate-host-ssh-cert.ts b/private-ca/server/src/generate-host-ssh-cert.ts index 154a6f9..f6b2fbc 100644 --- a/private-ca/server/src/generate-host-ssh-cert.ts +++ b/private-ca/server/src/generate-host-ssh-cert.ts @@ -6,7 +6,7 @@ import formatDate from './format-date.js'; import { CallerIdentityResponse, SecretData } from './types/index.js'; const exec = util.promisify(child_process.exec); -const validityInDays = parseInt(process.env.validityInDays ?? '1', 10); +const validityInDays = 7; export const signHostSSHCertificate = async ( callerIdentity: CallerIdentityResponse, From b2bcf459954d4556c0278c8f1b8e4d97bb7f4b35 Mon Sep 17 00:00:00 2001 From: giriparus Date: Wed, 23 Jul 2025 14:57:35 +0530 Subject: [PATCH 119/146] chore: default profile to empty string default region to eu-central-1 --- private-ca/update-server-on-lambda.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/private-ca/update-server-on-lambda.sh b/private-ca/update-server-on-lambda.sh index e29b646..941002a 100755 --- a/private-ca/update-server-on-lambda.sh +++ b/private-ca/update-server-on-lambda.sh @@ -1,8 +1,8 @@ #!/bin/bash FUNCTION_NAME=${1:-'privateCA'} -REGION=${2:-'ap-southeast-1'} -PROFILE=${3:-'default'} +REGION=${2:-'eu-central-1'} +PROFILE=${3:-''} cd server npm i @@ -17,4 +17,4 @@ aws lambda update-function-code \ --region "$REGION" \ --profile "$PROFILE" -rm -r lambda.zip \ No newline at end of file +rm -r lambda.zip From 01eab60682e96b431b6a5ee71e414aaded6f3a7f Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 23 Jul 2025 16:21:53 +0530 Subject: [PATCH 120/146] remove profile param, add check for cert file before deleting if RSA key not found --- .../client/generate-certificate-curl.sh | 28 ++++++++++--------- private-ca/update-server-on-lambda.sh | 10 +++---- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 46ced5f..10c748f 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -3,12 +3,11 @@ CA_ACTION=${1:-$CA_ACTION} CA_URL=${2:-$CA_URL} ENVIRONMENT=${3:-"client"} -AWS_PROFILE=${4:-"default"} -USER_SSH_DIR=${5:-"$HOME/.ssh"} -USER_AWS_DIR=${6:-"$HOME/.aws"} -SYSTEM_SSH_DIR=${7:-"/etc/ssh"} -AWS_STS_REGION=${8:-"ap-southeast-1"} -AWS_EC2_REGION=${9:-"us-west-2"} +USER_SSH_DIR=${4:-"$HOME/.ssh"} +USER_AWS_DIR=${5:-"$HOME/.aws"} +SYSTEM_SSH_DIR=${6:-"/etc/ssh"} +AWS_STS_REGION=${7:-"ap-southeast-1"} +AWS_EC2_REGION=${8:-"us-west-2"} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) [[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } @@ -49,9 +48,9 @@ get_aws_credentials() { [[ $? -ne 0 ]] && { echo "Your AWS credentials have either expired or are invalid. Please check your credentials and try again."; exit 1; } TEMP_CREDS=$(echo "{\"AccessKeyId\":\"$AWS_ACCESS_KEY_ID\",\"SecretAccessKey\":\"$AWS_SECRET_ACCESS_KEY\",\"Token\":\"$AWS_SESSION_TOKEN\"}") else - TEMP_CREDS=$(aws configure export-credentials --profile $AWS_PROFILE 2>/dev/null) + TEMP_CREDS=$(aws configure export-credentials 2>/dev/null) SESSION_TOKEN=$(echo "$TEMP_CREDS" | jq -r '.Token // .SessionToken // .Sessiontoken // empty') - [[ -z "$SESSION_TOKEN" ]] && TEMP_CREDS=$(aws sts get-session-token --profile $AWS_PROFILE | jq -r ".Credentials") + [[ -z "$SESSION_TOKEN" ]] && TEMP_CREDS=$(aws sts get-session-token | jq -r ".Credentials") fi else echo "Invalid environment provided. Allowed values are 'host' and 'client'"; exit 1; @@ -107,8 +106,8 @@ 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 "" - rm ${USER_SSH_DIR}/id_rsa-cert.pub + ssh-keygen -t rsa -b 4096 -f ${USER_SSH_DIR}/id_rsa -N "" + [[ -f ${USER_SSH_DIR}/id_rsa-cert.pub ]] && rm ${USER_SSH_DIR}/id_rsa-cert.pub } CERT_PUBKEY=$(cat ${USER_SSH_DIR}/id_rsa.pub | base64 | tr -d \\n) @@ -125,7 +124,7 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then # Check if a valid certificate exists CERT_VALID=false - half_life_seconds=302400 + half_life_seconds=259200 # 3 days if test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub; then current_timestamp=$(TZ=UTC date -u +"%Y-%m-%dT%H:%M:%S") certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub 2>/dev/null | awk '/Valid:/{print $NF}') @@ -141,7 +140,10 @@ 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 "" + test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key.pub || { + ssh-keygen -t rsa -b 4096 -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key -N "" + [[ -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub ]] && rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub + } CERT_PUBKEY=$(cat ${SYSTEM_SSH_DIR}/ssh_host_rsa_key.pub | base64 | tr -d \\n) else echo "Invalid Action" @@ -156,7 +158,7 @@ get_aws_credentials $ENVIRONMENT if [ ! -d "private-ca-client-env" ]; then $PYTHON_EXEC -m venv private-ca-client-env fi -source private-ca-client-env/bin/activate +source ./private-ca-client-env/bin/activate pip install -q --upgrade --disable-pip-version-check boto3 # Update PYTHON_EXEC to use the Python executable from the activated virtual environment diff --git a/private-ca/update-server-on-lambda.sh b/private-ca/update-server-on-lambda.sh index 941002a..d5f512a 100755 --- a/private-ca/update-server-on-lambda.sh +++ b/private-ca/update-server-on-lambda.sh @@ -4,17 +4,17 @@ FUNCTION_NAME=${1:-'privateCA'} REGION=${2:-'eu-central-1'} PROFILE=${3:-''} -cd server +[[ -n "$PROFILE" ]] && PROFILE="--profile $PROFILE" || PROFILE="" + +(cd server npm i npm run build -cd dist && zip -qr ../lambda.zip . && cd .. -mv lambda.zip ../ -cd .. +cd dist && zip -qr ../../lambda.zip .) aws lambda update-function-code \ --function-name "$FUNCTION_NAME" \ --zip-file fileb://lambda.zip \ --region "$REGION" \ - --profile "$PROFILE" + $PROFILE rm -r lambda.zip From f745ef3f0747d86c8eb87636dc00795cc8c6d4ec Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 23 Jul 2025 16:27:07 +0530 Subject: [PATCH 121/146] refactor: update CA keys comment, remove dependency on get-credentials in get-host-ca-public-key --- private-ca/client/get-host-ca-public-key.sh | 46 ++++++++++----------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/private-ca/client/get-host-ca-public-key.sh b/private-ca/client/get-host-ca-public-key.sh index 20ba2e9..1d6bc4b 100644 --- a/private-ca/client/get-host-ca-public-key.sh +++ b/private-ca/client/get-host-ca-public-key.sh @@ -1,7 +1,6 @@ #!/bin/bash CA_URL=${1:-$CA_URL} -AWS_PROFILE=${2:-"default"} USER_SSH_DIR=${3:-"$HOME/.ssh"} USER_AWS_DIR=${4:-"$HOME/.aws"} AWS_STS_REGION=${5:-"ap-southeast-1"} @@ -9,32 +8,24 @@ AWS_STS_REGION=${5:-"ap-southeast-1"} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) [[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } -if grep -q "^@cert-authority" "${USER_SSH_DIR}/known_hosts"; then +if grep -qE '^@cert-authority .* fundwave_host_ca$' "${USER_SSH_DIR}/known_hosts" 2>/dev/null; then echo "Host CA entry already present in known_hosts. Exiting." exit 0 +else + echo "Host CA entry not present in known_hosts. Adding it." fi -is_mfa_enabled() { - grep -q 'get-credentials' ${USER_AWS_DIR}/credentials -} - get_aws_credentials() { local TEMP_CREDS - if is_mfa_enabled; then - CALLER_IDENTITY=$(aws sts get-caller-identity --profile $AWS_PROFILE) + if [[ -n "$AWS_ACCESS_KEY_ID" && -n "$AWS_SECRET_ACCESS_KEY" && -n "$AWS_SESSION_TOKEN" ]]; then + CALLER_IDENTITY=$(aws sts get-caller-identity) [[ $? -ne 0 ]] && { echo "Your AWS credentials have either expired or are invalid. Please check your credentials and try again."; exit 1; } - - TEMP_CREDS=$(get-credentials $AWS_PROFILE) + TEMP_CREDS=$(echo "{\"AccessKeyId\":\"$AWS_ACCESS_KEY_ID\",\"SecretAccessKey\":\"$AWS_SECRET_ACCESS_KEY\",\"Token\":\"$AWS_SESSION_TOKEN\"}") else - #check if AWS creds are exposed as env variables, including AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN - if [[ -n "$AWS_ACCESS_KEY_ID" && -n "$AWS_SECRET_ACCESS_KEY" && -n "$AWS_SESSION_TOKEN" ]]; then - CALLER_IDENTITY=$(aws sts get-caller-identity) - [[ $? -ne 0 ]] && { echo "Your AWS credentials have either expired or are invalid. Please check your credentials and try again."; exit 1; } - TEMP_CREDS=$(echo "{\"AccessKeyId\":\"$AWS_ACCESS_KEY_ID\",\"SecretAccessKey\":\"$AWS_SECRET_ACCESS_KEY\",\"Token\":\"$AWS_SESSION_TOKEN\"}") - else - TEMP_CREDS=$(aws sts get-session-token --profile $AWS_PROFILE | jq -r ".Credentials") - fi + TEMP_CREDS=$(aws configure export-credentials 2>/dev/null) + SESSION_TOKEN=$(echo "$TEMP_CREDS" | jq -r '.Token // .SessionToken // .Sessiontoken // empty') + [[ -z "$SESSION_TOKEN" ]] && TEMP_CREDS=$(aws sts get-session-token | jq -r ".Credentials") fi ACCESS_KEY_ID=$(echo $TEMP_CREDS | jq -r ".AccessKeyId") @@ -42,12 +33,12 @@ get_aws_credentials() { SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".Token // .SessionToken // .Sessiontoken") } -get_aws_credentials $ENVIRONMENT +get_aws_credentials if [ ! -d "private-ca-client-env" ]; then $PYTHON_EXEC -m venv private-ca-client-env fi -source private-ca-client-env/bin/activate +source ./private-ca-client-env/bin/activate pip install -q --upgrade --disable-pip-version-check boto3 # Update PYTHON_EXEC to use the Python executable from the activated virtual environment @@ -89,12 +80,17 @@ HOST_CA_PUBKEY=$(echo $LAMBDA_RESPONSE | jq -r ".\"host_ca.pub\"" | base64 -d) [[ -f "${USER_SSH_DIR}/known_hosts" ]] || touch "${USER_SSH_DIR}/known_hosts" -# Add host CA public key to known_hosts file if it doesn't exist -if [[ $(grep -q "@cert-authority" "${USER_SSH_DIR}/known_hosts"; echo $?) -ne 0 ]]; then - # @cert-authority tells ssh to trust the host CA public key +# Add host CA public key to known_hosts file if it doesn't exist and update it if it does +# @cert-authority tells ssh to trust the host CA public key +# ${HOST_CA_PUBKEY} is the host CA public key that was used to sign the host certificate +if grep -qE '^@cert-authority .* fundwave_host_ca$' "${USER_SSH_DIR}/known_hosts"; then + # Update existing line + sed -i.bak -E "s|^(@cert-authority .*) ssh-rsa .*|\1 ${HOST_CA_PUBKEY}|" "${USER_SSH_DIR}/known_hosts" +else + # Add new line # * means all hosts (wildcard) (you can also specify a list of comma separated hostnames) - # ${HOST_CA_PUBKEY} is the host CA public key that was used to sign the host certificate - echo "@cert-authority * ${HOST_CA_PUBKEY}" >> ${USER_SSH_DIR}/known_hosts + echo "@cert-authority * ${HOST_CA_PUBKEY}" >> "${USER_SSH_DIR}/known_hosts" fi +echo "Host CA public key added to known_hosts." deactivate \ No newline at end of file From 74c438779de45ac313e8d903c9ead043a8542b46 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 23 Jul 2025 16:44:04 +0530 Subject: [PATCH 122/146] remove node-forge dependency --- private-ca/server/package-lock.json | 12 +----------- private-ca/server/package.json | 3 +-- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/private-ca/server/package-lock.json b/private-ca/server/package-lock.json index 934d8a1..90f858b 100644 --- a/private-ca/server/package-lock.json +++ b/private-ca/server/package-lock.json @@ -10,8 +10,7 @@ "license": "ISC", "dependencies": { "@aws-sdk/client-ec2": "^3.827.0", - "@aws-sdk/client-secrets-manager": "^3.825.0", - "node-forge": "^1.3.1" + "@aws-sdk/client-secrets-manager": "^3.825.0" }, "devDependencies": { "@types/node": "^20.10.5", @@ -1822,15 +1821,6 @@ "fxparser": "src/cli/cli.js" } }, - "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==", - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, "node_modules/strnum": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", diff --git a/private-ca/server/package.json b/private-ca/server/package.json index 1f2d12a..abb1215 100644 --- a/private-ca/server/package.json +++ b/private-ca/server/package.json @@ -13,8 +13,7 @@ "license": "ISC", "dependencies": { "@aws-sdk/client-ec2": "^3.827.0", - "@aws-sdk/client-secrets-manager": "^3.825.0", - "node-forge": "^1.3.1" + "@aws-sdk/client-secrets-manager": "^3.825.0" }, "devDependencies": { "@types/node": "^20.10.5", From 9cb6dc84ac81a9e9a2f3b24c95ecd90a9991ecf3 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Wed, 23 Jul 2025 17:51:00 +0530 Subject: [PATCH 123/146] fix: update format for current_timestamp var to use epoch time --- private-ca/client/generate-certificate-curl.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 10c748f..9e23b44 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -85,7 +85,7 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then CERT_VALID=false if test -f ${USER_SSH_DIR}/id_rsa-cert.pub; then # Client SSH Certificate already exists - current_timestamp=$(TZ=UTC date -u +"%Y-%m-%dT%H:%M:%S") + current_timestamp=$(date -u +%s) certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${USER_SSH_DIR}/id_rsa-cert.pub 2>/dev/null | awk '/Valid:/{print $NF}') if [[ $certificate_expiration_timestamp > $current_timestamp ]]; then @@ -126,7 +126,7 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then CERT_VALID=false half_life_seconds=259200 # 3 days if test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub; then - current_timestamp=$(TZ=UTC date -u +"%Y-%m-%dT%H:%M:%S") + current_timestamp=$(date -u +%s) certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub 2>/dev/null | awk '/Valid:/{print $NF}') [[ $(uname) == "Darwin" ]] && cert_expiry_epoch=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$certificate_expiration_timestamp" +"%s") || cert_expiry_epoch=$(date -d "$certificate_expiration_timestamp" +"%s") next_run_timestamp=$((current_timestamp + half_life_seconds)) From c1740ffda5714e3bdc8d5e2ab453a495480e6325 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Thu, 24 Jul 2025 13:14:45 +0530 Subject: [PATCH 124/146] fix: correct identifier of user CA public key --- private-ca/client/generate-certificate-curl.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 9e23b44..1aa57e9 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -315,9 +315,9 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then [[ -f "${SYSTEM_SSH_DIR}/user_ca.pub" ]] || touch "${SYSTEM_SSH_DIR}/user_ca.pub" - if grep -qE '.* fundwave_host_ca$' "${SYSTEM_SSH_DIR}/user_ca.pub"; then + if grep -qE '.* fundwave_user_ca$' "${SYSTEM_SSH_DIR}/user_ca.pub"; then # Update existing line - sed -i.bak -E "s|ssh-rsa .* fundwave_host_ca$|${USER_CA_PUBKEY}|" "${SYSTEM_SSH_DIR}/user_ca.pub" + sed -i.bak -E "s|ssh-rsa .* fundwave_user_ca$|${USER_CA_PUBKEY}|" "${SYSTEM_SSH_DIR}/user_ca.pub" else # Add new line # * means all hosts (wildcard) (you can also specify a list of comma separated hostnames) From a77f2f7c8eac95936a4c003f0ae4cec7afe7675f Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Thu, 24 Jul 2025 23:45:29 +0530 Subject: [PATCH 125/146] refactor: update echo msgs --- .../client/generate-certificate-curl.sh | 61 +++++++------------ 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 1aa57e9..4020647 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -15,20 +15,24 @@ PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) trap 'clean_config_on_error' EXIT clean_config_on_error() { - if [[ $? -ne 0 && $CA_ACTION == "generateHostSSHCert" && $CERT_VALID == "false" ]]; then - echo "Error occurred. Cleaning host SSH config..." - sed -i "\|^HostCertificate ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config - sed -i "\|^TrustedUserCAKeys ${SYSTEM_SSH_DIR}/user_ca.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config - - rm ${SYSTEM_SSH_DIR}/user_ca.pub - rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub - systemctl restart sshd - echo "Host SSH config cleaned." + if [[ $? -ne 0 && $CA_ACTION == "generateHostSSHCert" ]]; then + if [[ "$CERT_VALID" == "true" ]]; then + echo "Keeping existing valid certificate." + else + echo "Error occurred. Cleaning host SSH config..." + sed -i "\|^HostCertificate ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config + sed -i "\|^TrustedUserCAKeys ${SYSTEM_SSH_DIR}/user_ca.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config + + rm ${SYSTEM_SSH_DIR}/user_ca.pub + rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub + systemctl restart sshd + echo "Host SSH config cleaned." + fi fi } get_aws_credentials() { - local method=${1:-"host"} + local ENVIRONMENT=${1:-"host"} local TEMP_CREDS if [[ $method == "host" ]]; then @@ -93,15 +97,15 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then if [[ -f "${USER_SSH_DIR}/known_hosts" ]]; then if grep -qE '^@cert-authority .* fundwave_host_ca$' "${USER_SSH_DIR}/known_hosts"; then CERT_VALID=true - echo "A valid certificate and known_hosts entry were found." + echo "A valid user certificate and known_hosts entry were found." else - echo "Certificate is valid, but known_hosts entry is missing." + echo "User certificate is valid, but known_hosts entry is missing." fi else - echo "Certificate is valid, but known_hosts file is missing." + echo "User certificate is valid, but known_hosts file is missing." fi else - echo "Existing certificate is expired or invalid." + echo "Existing user certificate is expired or invalid." rm -f ${USER_SSH_DIR}/id_rsa-cert.pub fi fi @@ -133,9 +137,9 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then if [[ $cert_expiry_epoch -gt $next_run_timestamp ]]; then CERT_VALID=true - echo "A valid certificate was found at ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub." + echo "A valid host certificate was found at ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub." else - echo "Existing certificate will expire before next cron run." + echo "Existing host certificate will expire before next cron run." rm -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub fi fi @@ -195,17 +199,12 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then if [[ "$STATUS_CODE" != "200" ]]; then echo "CA request failed (Status: ${STATUS_CODE}): ${LAMBDA_RESPONSE}" - if [[ "$CERT_VALID" == "true" ]]; then - echo "Keeping existing valid certificate." - fi + exit 1; fi ENCODED_CERTIFICATE=$(echo "$LAMBDA_RESPONSE" | jq -er ".certificate") || { echo "Certificate not found in Lambda response. Aborting."; - if [[ "$CERT_VALID" == "true" ]]; then - echo "Keeping existing valid certificate." - fi exit 1; } @@ -214,9 +213,6 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then if [[ -z "$CERTIFICATE" ]]; then echo "Empty certificate received. Not writing to disk." - if [[ "$CERT_VALID" == "true" ]]; then - echo "Keeping existing valid certificate." - fi exit 1 fi @@ -234,9 +230,6 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then # New certificate is invalid rm -f "$TEMP_CERT_FILE" echo "Generated certificate is invalid. Discarding." - if [[ "$CERT_VALID" == "true" ]]; then - echo "Keeping existing valid certificate." - fi exit 1 fi @@ -268,17 +261,11 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then if [[ "$STATUS_CODE" != "200" ]]; then echo "CA request failed (Status: ${STATUS_CODE}): ${LAMBDA_RESPONSE}" - if [[ "$CERT_VALID" == "true" ]]; then - echo "Keeping existing valid certificate." - fi exit 1; fi ENCODED_CERTIFICATE=$(echo "$LAMBDA_RESPONSE" | jq -er ".certificate") || { echo "Certificate not found in Lambda response. Aborting."; - if [[ "$CERT_VALID" == "true" ]]; then - echo "Keeping existing valid certificate." - fi exit 1; } @@ -287,9 +274,6 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then if [[ -z "$CERTIFICATE" ]]; then echo "Empty certificate received. Not writing to disk." - if [[ "$CERT_VALID" == "true" ]]; then - echo "Keeping existing valid certificate." - fi exit 1 fi @@ -307,9 +291,6 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then # New certificate is invalid rm -f "$TEMP_CERT_FILE" echo "Generated certificate is invalid. Discarding." - if [[ "$CERT_VALID" == "true" ]]; then - echo "Keeping existing valid certificate." - fi exit 1 fi From b670a5dff8a9ccc2978d21d8391e53c060a1c4f2 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 00:36:55 +0530 Subject: [PATCH 126/146] refactor [major]: create reusable functions and remove duplicate code --- .../client/generate-certificate-curl.sh | 259 ++++++++---------- 1 file changed, 113 insertions(+), 146 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 4020647..750a9f1 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -32,10 +32,9 @@ clean_config_on_error() { } get_aws_credentials() { - local ENVIRONMENT=${1:-"host"} local TEMP_CREDS - if [[ $method == "host" ]]; then + if [[ $ENVIRONMENT == "host" ]]; then TOKEN=$(curl -s --max-time 30 -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 120") if [[ -z "$TOKEN" ]]; then @@ -46,7 +45,7 @@ get_aws_credentials() { INSTANCE_ROLE_NAME=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/) TEMP_CREDS=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$INSTANCE_ROLE_NAME) - elif [[ $method == "client" ]]; then + elif [[ $ENVIRONMENT == "client" ]]; then if [[ -n "$AWS_ACCESS_KEY_ID" && -n "$AWS_SECRET_ACCESS_KEY" && -n "$AWS_SESSION_TOKEN" ]]; then CALLER_IDENTITY=$(aws sts get-caller-identity) [[ $? -ne 0 ]] && { echo "Your AWS credentials have either expired or are invalid. Please check your credentials and try again."; exit 1; } @@ -65,6 +64,85 @@ get_aws_credentials() { SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".Token // .SessionToken // .Sessiontoken") } +prepare_event_json() { + # Setup Python virtual environment + if [ ! -d "private-ca-client-env" ]; then + $PYTHON_EXEC -m venv private-ca-client-env + fi + source ./private-ca-client-env/bin/activate + pip install -q --upgrade --disable-pip-version-check boto3 + + # Update PYTHON_EXEC to use the Python executable from the activated virtual environment + # This ensures we use the venv's Python with the installed dependencies (boto3) + PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) + + # Generate Auth Headers + output=$($PYTHON_EXEC aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_STS_REGION) || { + echo "Failed to generate auth header. Check aws-auth-header.py script."; + exit 1; + } + auth_header=$(echo "$output" | jq -er ".Authorization") || { + echo "Failed to parse Authorization from auth-header output."; + exit 1; + } + date=$(echo "$output" | jq -er ".Date") || { + echo "Failed to parse Date from auth-header output."; + exit 1; + } + + # Create event JSON for CA request + EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\",\"awsEC2Region\":\"${AWS_EC2_REGION}\"}") +} + +invoke_lambda() { + read -r STATUS_CODE LAMBDA_RESPONSE < <( + curl -s "${CA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" -w "%{http_code}\n" | + { + response=$(cat) + status_code=${response: -3} + body=${response:0:$((${#response}-3))} + echo "$status_code $body" + } + ) + + if [[ "$STATUS_CODE" != "200" ]]; then + echo "CA request failed (Status: ${STATUS_CODE}): ${LAMBDA_RESPONSE}" + exit 1; + fi + + ENCODED_CERTIFICATE=$(echo "$LAMBDA_RESPONSE" | jq -er ".certificate") || { + echo "Certificate not found in Lambda response. Aborting."; + exit 1; + } + + CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) + + if [[ -z "$CERTIFICATE" ]]; then + echo "Invalid certificate received. Aborting." + exit 1 + fi +} + +safe_replace_old_certificate() { + local CERTIFICATE=${1} + local CERT_FILE_PATH=${2} + + local TEMP_CERT_FILE="${CERT_FILE_PATH}.tmp" + + echo "$CERTIFICATE" > "$TEMP_CERT_FILE" + + # Verify the new certificate is valid + if ssh-keygen -Lf "$TEMP_CERT_FILE" >/dev/null 2>&1; then + mv "$TEMP_CERT_FILE" "${CERT_FILE_PATH}" + echo "New certificate written to ${CERT_FILE_PATH}" + CERT_VALID=true + else + rm -f "$TEMP_CERT_FILE" + echo "Generated certificate is invalid. Discarding." + exit 1 + fi +} + # Check for options while getopts ":h" option; do case $option in @@ -115,10 +193,31 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then } CERT_PUBKEY=$(cat ${USER_SSH_DIR}/id_rsa.pub | base64 | tr -d \\n) + get_aws_credentials + prepare_event_json + invoke_lambda + + HOST_CA_PUBKEY=$(echo $LAMBDA_RESPONSE | jq -r ".\"host_ca.pub\"" | base64 -d) + + safe_replace_old_certificate "$CERTIFICATE" "${USER_SSH_DIR}/id_rsa-cert.pub" + + [[ -f "${USER_SSH_DIR}/known_hosts" ]] || touch "${USER_SSH_DIR}/known_hosts" + + # Add host CA public key to known_hosts file if it doesn't exist and update it if it does + # @cert-authority tells ssh to trust the host CA public key + # ${HOST_CA_PUBKEY} is the host CA public key that was used to sign the host certificate + if grep -qE '^@cert-authority .* fundwave_host_ca$' "${USER_SSH_DIR}/known_hosts"; then + # Update existing line + sed -i.bak -E "s|^(@cert-authority .*) ssh-rsa .*|\1 ${HOST_CA_PUBKEY}|" "${USER_SSH_DIR}/known_hosts" + else + # * means all hosts (wildcard) (you can also specify a list of comma separated hostnames) + echo "@cert-authority * ${HOST_CA_PUBKEY}" >> "${USER_SSH_DIR}/known_hosts" + fi + elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then # Host certificate generation is not allowed in client environment if [[ $ENVIRONMENT = "client" ]]; then - echo -e "\nError: generateHostSSHCert is not allowed in client environment.\nHost certificate generation requires host/server environment.\n" + echo -e "\nError: generateHostSSHCert is not allowed in client environment.\nHost certificate generation requires host (server) environment.\n" exit 1 fi if [ "$EUID" -ne 0 ]; then @@ -149,159 +248,21 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then [[ -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub ]] && rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub } CERT_PUBKEY=$(cat ${SYSTEM_SSH_DIR}/ssh_host_rsa_key.pub | 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" - exit 1; -fi - -get_aws_credentials $ENVIRONMENT - -if [ ! -d "private-ca-client-env" ]; then - $PYTHON_EXEC -m venv private-ca-client-env -fi -source ./private-ca-client-env/bin/activate -pip install -q --upgrade --disable-pip-version-check boto3 -# Update PYTHON_EXEC to use the Python executable from the activated virtual environment -# This ensures we use the venv's Python with the installed dependencies (boto3) -PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) + get_aws_credentials + prepare_event_json + invoke_lambda -# Auth Headers -output=$($PYTHON_EXEC aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_STS_REGION) || { - echo "Failed to generate auth header. Check aws-auth-header.py script."; - exit 1; -} -auth_header=$(echo "$output" | jq -er ".Authorization") || { - echo "Failed to parse Authorization from auth-header output."; - exit 1; -} -date=$(echo "$output" | jq -er ".Date") || { - echo "Failed to parse Date from auth-header output."; - exit 1; -} - -EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\",\"awsEC2Region\":\"${AWS_EC2_REGION}\"}") - - -if [[ $CA_ACTION = "generateClientSSHCert" ]]; then - read -r STATUS_CODE LAMBDA_RESPONSE < <( - curl -s "${CA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" -w "%{http_code}\n" | - { - response=$(cat) - status_code=${response: -3} - body=${response:0:$((${#response}-3))} - echo "$status_code $body" - } - ) - - if [[ "$STATUS_CODE" != "200" ]]; then - echo "CA request failed (Status: ${STATUS_CODE}): ${LAMBDA_RESPONSE}" - - exit 1; - fi - - ENCODED_CERTIFICATE=$(echo "$LAMBDA_RESPONSE" | jq -er ".certificate") || { - echo "Certificate not found in Lambda response. Aborting."; - exit 1; - } - - CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) - HOST_CA_PUBKEY=$(echo $LAMBDA_RESPONSE | jq -r ".\"host_ca.pub\"" | base64 -d) - - if [[ -z "$CERTIFICATE" ]]; then - echo "Empty certificate received. Not writing to disk." - exit 1 - fi - - # Write new certificate to temporary file first - TEMP_CERT_FILE="${USER_SSH_DIR}/id_rsa-cert.pub.tmp" - echo "$CERTIFICATE" > "$TEMP_CERT_FILE" - - # Verify the new certificate is valid - if ssh-keygen -Lf "$TEMP_CERT_FILE" >/dev/null 2>&1; then - # New certificate is valid, replace the old one - mv "$TEMP_CERT_FILE" "${USER_SSH_DIR}/id_rsa-cert.pub" - echo "New certificate written to ${USER_SSH_DIR}/id_rsa-cert.pub" - CERT_VALID=true - else - # New certificate is invalid - rm -f "$TEMP_CERT_FILE" - echo "Generated certificate is invalid. Discarding." - exit 1 - fi - - [[ -f "${USER_SSH_DIR}/known_hosts" ]] || touch "${USER_SSH_DIR}/known_hosts" - - # Add host CA public key to known_hosts file if it doesn't exist and update it if it does - # @cert-authority tells ssh to trust the host CA public key - # ${HOST_CA_PUBKEY} is the host CA public key that was used to sign the host certificate - if grep -qE '^@cert-authority .* fundwave_host_ca$' "${USER_SSH_DIR}/known_hosts"; then - # Update existing line - sed -i.bak -E "s|^(@cert-authority .*) ssh-rsa .*|\1 ${HOST_CA_PUBKEY}|" "${USER_SSH_DIR}/known_hosts" - else - # Add new line - # * means all hosts (wildcard) (you can also specify a list of comma separated hostnames) - echo "@cert-authority * ${HOST_CA_PUBKEY}" >> "${USER_SSH_DIR}/known_hosts" - fi - -# sudo access is required to generate host certificate -elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then - read -r STATUS_CODE LAMBDA_RESPONSE < <( - curl -s "${CA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" -w "%{http_code}\n" | - { - response=$(cat) - status_code=${response: -3} - body=${response:0:$((${#response}-3))} - echo "$status_code $body" - } - ) - - if [[ "$STATUS_CODE" != "200" ]]; then - echo "CA request failed (Status: ${STATUS_CODE}): ${LAMBDA_RESPONSE}" - exit 1; - fi - - ENCODED_CERTIFICATE=$(echo "$LAMBDA_RESPONSE" | jq -er ".certificate") || { - echo "Certificate not found in Lambda response. Aborting."; - exit 1; - } - - CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) USER_CA_PUBKEY=$(echo $LAMBDA_RESPONSE | jq -r ".\"user_ca.pub\"" | base64 -d) - if [[ -z "$CERTIFICATE" ]]; then - echo "Empty certificate received. Not writing to disk." - exit 1 - fi - - # Write new certificate to temporary file first - TEMP_CERT_FILE="${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub.tmp" - echo "$CERTIFICATE" > "$TEMP_CERT_FILE" + safe_replace_old_certificate "$CERTIFICATE" "${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" - # Verify the new certificate is valid - if ssh-keygen -Lf "$TEMP_CERT_FILE" >/dev/null 2>&1; then - # New certificate is valid, replace the old one - mv "$TEMP_CERT_FILE" "${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" - echo "New certificate written to ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" - CERT_VALID=true - else - # New certificate is invalid - rm -f "$TEMP_CERT_FILE" - echo "Generated certificate is invalid. Discarding." - exit 1 - fi - [[ -f "${SYSTEM_SSH_DIR}/user_ca.pub" ]] || touch "${SYSTEM_SSH_DIR}/user_ca.pub" if grep -qE '.* fundwave_user_ca$' "${SYSTEM_SSH_DIR}/user_ca.pub"; then # Update existing line sed -i.bak -E "s|ssh-rsa .* fundwave_user_ca$|${USER_CA_PUBKEY}|" "${SYSTEM_SSH_DIR}/user_ca.pub" else - # Add new line - # * means all hosts (wildcard) (you can also specify a list of comma separated hostnames) echo "${USER_CA_PUBKEY}" >> "${SYSTEM_SSH_DIR}/user_ca.pub" fi @@ -313,6 +274,12 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then echo "TrustedUserCAKeys ${SYSTEM_SSH_DIR}/user_ca.pub" >> ${SYSTEM_SSH_DIR}/sshd_config fi systemctl restart sshd +else + echo "Invalid Action" + echo "Possible actions include:" + echo " generateHostSSHCert: Generates SSH Certificate for Host" + echo " generateClientSSHCert: Generates SSH Certificate for Client" + exit 1; fi deactivate \ No newline at end of file From 4a1396a467e2d997669b7d63328c4d048bd21f1c Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 00:50:15 +0530 Subject: [PATCH 127/146] fix: update usage instructions in generate-certificate-curl.sh --- private-ca/client/generate-certificate-curl.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 750a9f1..b1e2405 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -147,7 +147,7 @@ safe_replace_old_certificate() { while getopts ":h" option; do case $option in h) - echo "Usage: ./generate-certificate.sh [ACTION] [CA URL] [ENVIRONMENT] [AWS PROFILE] [USER SSH DIR] [USER AWS DIR] [SYSTEM SSH DIR] [AWS STS REGION]" + echo "Usage: bash generate-certificate-curl.sh [ACTION] [CA URL] [ENVIRONMENT] [USER SSH DIR] [USER AWS DIR] [SYSTEM SSH DIR] [AWS STS REGION] [AWS EC2 REGION]" echo "Possible actions:" echo " generateHostSSHCert: Generates SSH Certificate for Host" echo " generateClientSSHCert: Generates SSH Certificate for Client" @@ -274,6 +274,7 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then echo "TrustedUserCAKeys ${SYSTEM_SSH_DIR}/user_ca.pub" >> ${SYSTEM_SSH_DIR}/sshd_config fi systemctl restart sshd + else echo "Invalid Action" echo "Possible actions include:" From 7da5c7535a8b7932ccb800d7547095306d1c383c Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 01:01:05 +0530 Subject: [PATCH 128/146] update AWS region defaults to eu-central-1 --- private-ca/client/generate-certificate-curl.sh | 4 ++-- private-ca/deploy-server-on-lambda.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index b1e2405..7c59f0d 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -6,8 +6,8 @@ ENVIRONMENT=${3:-"client"} USER_SSH_DIR=${4:-"$HOME/.ssh"} USER_AWS_DIR=${5:-"$HOME/.aws"} SYSTEM_SSH_DIR=${6:-"/etc/ssh"} -AWS_STS_REGION=${7:-"ap-southeast-1"} -AWS_EC2_REGION=${8:-"us-west-2"} +AWS_STS_REGION=${7:-"eu-central-1"} +AWS_EC2_REGION=${8:-"eu-central-1"} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) [[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } diff --git a/private-ca/deploy-server-on-lambda.sh b/private-ca/deploy-server-on-lambda.sh index 0d19875..78ba953 100755 --- a/private-ca/deploy-server-on-lambda.sh +++ b/private-ca/deploy-server-on-lambda.sh @@ -6,7 +6,7 @@ ROLE_NAME=${2:-"privateCALambdaRole"} POLICY_NAME=${3:-"PrivateCAPolicy"} LAYER_NAME=${4:-"openssh"} FUNCTION_NAME=${5:-"privateCA"} -AWS_REGION=${6:-"ap-southeast-1"} +AWS_REGION=${6:-"eu-central-1"} AWS_PROFILE=${7:-"default"} ################## Secret ################## From a4159693526bdc5cbbd43a56f4bf588a0c51d421 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 01:35:09 +0530 Subject: [PATCH 129/146] refactor: remove default values of CA Action and URL --- private-ca/client/generate-certificate-curl.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index 7c59f0d..678d87c 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -1,7 +1,7 @@ #!/bin/bash -CA_ACTION=${1:-$CA_ACTION} -CA_URL=${2:-$CA_URL} +CA_ACTION=${1} +CA_URL=${2} ENVIRONMENT=${3:-"client"} USER_SSH_DIR=${4:-"$HOME/.ssh"} USER_AWS_DIR=${5:-"$HOME/.aws"} From 79734b354592910a23785fbb5bbca936a93d012e Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 01:51:15 +0530 Subject: [PATCH 130/146] refactor: use date-fns dependency and remove custom date formatting --- private-ca/server/package-lock.json | 13 ++++++++++++- private-ca/server/package.json | 7 ++++--- private-ca/server/src/format-date.ts | 12 ------------ private-ca/server/src/generate-client-ssh-cert.ts | 6 +++--- private-ca/server/src/generate-host-ssh-cert.ts | 8 ++++---- 5 files changed, 23 insertions(+), 23 deletions(-) delete mode 100644 private-ca/server/src/format-date.ts diff --git a/private-ca/server/package-lock.json b/private-ca/server/package-lock.json index 90f858b..f5248e6 100644 --- a/private-ca/server/package-lock.json +++ b/private-ca/server/package-lock.json @@ -10,7 +10,8 @@ "license": "ISC", "dependencies": { "@aws-sdk/client-ec2": "^3.827.0", - "@aws-sdk/client-secrets-manager": "^3.825.0" + "@aws-sdk/client-secrets-manager": "^3.825.0", + "date-fns": "^4.1.0" }, "devDependencies": { "@types/node": "^20.10.5", @@ -1758,6 +1759,16 @@ "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", "license": "MIT" }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/esbuild": { "version": "0.25.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", diff --git a/private-ca/server/package.json b/private-ca/server/package.json index abb1215..cf1cbd2 100644 --- a/private-ca/server/package.json +++ b/private-ca/server/package.json @@ -13,11 +13,12 @@ "license": "ISC", "dependencies": { "@aws-sdk/client-ec2": "^3.827.0", - "@aws-sdk/client-secrets-manager": "^3.825.0" + "@aws-sdk/client-secrets-manager": "^3.825.0", + "date-fns": "^4.1.0" }, "devDependencies": { "@types/node": "^20.10.5", - "typescript": "^5.3.3", - "esbuild": "^0.25.5" + "esbuild": "^0.25.5", + "typescript": "^5.3.3" } } diff --git a/private-ca/server/src/format-date.ts b/private-ca/server/src/format-date.ts deleted file mode 100644 index 0505020..0000000 --- a/private-ca/server/src/format-date.ts +++ /dev/null @@ -1,12 +0,0 @@ -const formatDate = (date: Date): string => { - const year = date.getFullYear(); - const month = String(date.getMonth() + 1).padStart(2, '0'); - const day = String(date.getDate()).padStart(2, '0'); - const hours = String(date.getHours()).padStart(2, '0'); - const minutes = String(date.getMinutes()).padStart(2, '0'); - const seconds = String(date.getSeconds()).padStart(2, '0'); - - return `${year}${month}${day}${hours}${minutes}${seconds}`; -}; - -export default formatDate; \ No newline at end of file diff --git a/private-ca/server/src/generate-client-ssh-cert.ts b/private-ca/server/src/generate-client-ssh-cert.ts index e5848f3..1e556e8 100644 --- a/private-ca/server/src/generate-client-ssh-cert.ts +++ b/private-ca/server/src/generate-client-ssh-cert.ts @@ -1,7 +1,7 @@ import fs from 'fs'; import child_process from 'child_process'; import util from 'util'; -import formatDate from './format-date.js'; +import { format } from 'date-fns'; import { CallerIdentityResponse, SecretData } from './types/index.js'; const exec = util.promisify(child_process.exec); @@ -33,10 +33,10 @@ export const signClientSSHCertificate = async ( console.log('stderr:', result.stderr); const now = new Date(); - const validFrom = formatDate(now); + const validFrom = format(now, "yyyyMMddHHmmss"); const validUntil = new Date(now.getTime() + (validityInDays * 24 * 60 * 60 * 1000)); - const validTo = formatDate(validUntil); + const validTo = format(validUntil, "yyyyMMddHHmmss"); const validityPeriod = `${validFrom}:${validTo}`; diff --git a/private-ca/server/src/generate-host-ssh-cert.ts b/private-ca/server/src/generate-host-ssh-cert.ts index f6b2fbc..ada7763 100644 --- a/private-ca/server/src/generate-host-ssh-cert.ts +++ b/private-ca/server/src/generate-host-ssh-cert.ts @@ -2,7 +2,7 @@ import fs from 'fs'; import child_process from 'child_process'; import util from 'util'; import { getPublicIpAddress } from './get-public-ip-address.js'; -import formatDate from './format-date.js'; +import { format } from 'date-fns'; import { CallerIdentityResponse, SecretData } from './types/index.js'; const exec = util.promisify(child_process.exec); @@ -38,10 +38,10 @@ export const signHostSSHCertificate = async ( console.log('stderr:', result.stderr); const now = new Date(); - const validFrom = formatDate(now); + const validFrom = format(now, "yyyyMMddHHmmss"); - const validUntil = new Date(now.getTime() + (validityInDays * 24 * 60 * 60 * 1000)); - const validTo = formatDate(validUntil); + const validUntil = new Date(now.getTime() + (60 * 1000)); + const validTo = format(validUntil, "yyyyMMddHHmmss"); const validityPeriod = `${validFrom}:${validTo}`; From 982f4108cd51a1c0af14a125cee946cce9448355 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 02:18:11 +0530 Subject: [PATCH 131/146] refactor: rename generate-certificate-curl.sh to invoke-private-ca.sh, add get-host-ca functionality in it, remove extra file --- private-ca/client/get-host-ca-public-key.sh | 96 ------------------- ...rtificate-curl.sh => invoke-private-ca.sh} | 20 ++++ 2 files changed, 20 insertions(+), 96 deletions(-) delete mode 100644 private-ca/client/get-host-ca-public-key.sh rename private-ca/client/{generate-certificate-curl.sh => invoke-private-ca.sh} (93%) diff --git a/private-ca/client/get-host-ca-public-key.sh b/private-ca/client/get-host-ca-public-key.sh deleted file mode 100644 index 1d6bc4b..0000000 --- a/private-ca/client/get-host-ca-public-key.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/bin/bash - -CA_URL=${1:-$CA_URL} -USER_SSH_DIR=${3:-"$HOME/.ssh"} -USER_AWS_DIR=${4:-"$HOME/.aws"} -AWS_STS_REGION=${5:-"ap-southeast-1"} - -PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) -[[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } - -if grep -qE '^@cert-authority .* fundwave_host_ca$' "${USER_SSH_DIR}/known_hosts" 2>/dev/null; then - echo "Host CA entry already present in known_hosts. Exiting." - exit 0 -else - echo "Host CA entry not present in known_hosts. Adding it." -fi - -get_aws_credentials() { - local TEMP_CREDS - - if [[ -n "$AWS_ACCESS_KEY_ID" && -n "$AWS_SECRET_ACCESS_KEY" && -n "$AWS_SESSION_TOKEN" ]]; then - CALLER_IDENTITY=$(aws sts get-caller-identity) - [[ $? -ne 0 ]] && { echo "Your AWS credentials have either expired or are invalid. Please check your credentials and try again."; exit 1; } - TEMP_CREDS=$(echo "{\"AccessKeyId\":\"$AWS_ACCESS_KEY_ID\",\"SecretAccessKey\":\"$AWS_SECRET_ACCESS_KEY\",\"Token\":\"$AWS_SESSION_TOKEN\"}") - else - TEMP_CREDS=$(aws configure export-credentials 2>/dev/null) - SESSION_TOKEN=$(echo "$TEMP_CREDS" | jq -r '.Token // .SessionToken // .Sessiontoken // empty') - [[ -z "$SESSION_TOKEN" ]] && TEMP_CREDS=$(aws sts get-session-token | jq -r ".Credentials") - fi - - 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 // .SessionToken // .Sessiontoken") -} - -get_aws_credentials - -if [ ! -d "private-ca-client-env" ]; then - $PYTHON_EXEC -m venv private-ca-client-env -fi -source ./private-ca-client-env/bin/activate -pip install -q --upgrade --disable-pip-version-check boto3 - -# Update PYTHON_EXEC to use the Python executable from the activated virtual environment -# This ensures we use the venv's Python with the installed dependencies (boto3) -PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) - -# Auth Headers -output=$($PYTHON_EXEC aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_STS_REGION) || { - echo "Failed to generate auth header. Check aws-auth-header.py script."; - exit 1; -} -auth_header=$(echo "$output" | jq -er ".Authorization") || { - echo "Failed to parse Authorization from auth-header output."; - exit 1; -} -date=$(echo "$output" | jq -er ".Date") || { - echo "Failed to parse Date from auth-header output."; - exit 1; -} - -EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"}, \"action\":\"getHostCAPublicKey\", \"awsSTSRegion\":\"${AWS_STS_REGION}\"}") - -read -r STATUS_CODE LAMBDA_RESPONSE < <( - curl -s "${CA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" -w "%{http_code}\n" | - { - response=$(cat) - status_code=${response: -3} - body=${response:0:$((${#response}-3))} - echo "$status_code $body" - } -) - -if [[ "$STATUS_CODE" != "200" ]]; then - echo "CA request failed (Status: ${STATUS_CODE}): ${LAMBDA_RESPONSE}" - exit 1; -fi - -HOST_CA_PUBKEY=$(echo $LAMBDA_RESPONSE | jq -r ".\"host_ca.pub\"" | base64 -d) - -[[ -f "${USER_SSH_DIR}/known_hosts" ]] || touch "${USER_SSH_DIR}/known_hosts" - -# Add host CA public key to known_hosts file if it doesn't exist and update it if it does -# @cert-authority tells ssh to trust the host CA public key -# ${HOST_CA_PUBKEY} is the host CA public key that was used to sign the host certificate -if grep -qE '^@cert-authority .* fundwave_host_ca$' "${USER_SSH_DIR}/known_hosts"; then - # Update existing line - sed -i.bak -E "s|^(@cert-authority .*) ssh-rsa .*|\1 ${HOST_CA_PUBKEY}|" "${USER_SSH_DIR}/known_hosts" -else - # Add new line - # * means all hosts (wildcard) (you can also specify a list of comma separated hostnames) - echo "@cert-authority * ${HOST_CA_PUBKEY}" >> "${USER_SSH_DIR}/known_hosts" -fi -echo "Host CA public key added to known_hosts." - -deactivate \ No newline at end of file diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/invoke-private-ca.sh similarity index 93% rename from private-ca/client/generate-certificate-curl.sh rename to private-ca/client/invoke-private-ca.sh index 678d87c..5736377 100644 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/invoke-private-ca.sh @@ -109,6 +109,9 @@ invoke_lambda() { echo "CA request failed (Status: ${STATUS_CODE}): ${LAMBDA_RESPONSE}" exit 1; fi + + # If the action is getHostCAPublicKey, we don't get a certificate + [[ $CA_ACTION == "getHostCAPublicKey" ]] && return ENCODED_CERTIFICATE=$(echo "$LAMBDA_RESPONSE" | jq -er ".certificate") || { echo "Certificate not found in Lambda response. Aborting."; @@ -275,11 +278,28 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then fi systemctl restart sshd +elif [[ $CA_ACTION = "getHostCAPublicKey" ]]; then + get_aws_credentials + prepare_event_json + invoke_lambda + + HOST_CA_PUBKEY=$(echo $LAMBDA_RESPONSE | jq -r ".\"host_ca.pub\"" | base64 -d) + + [[ -f "${USER_SSH_DIR}/known_hosts" ]] || touch "${USER_SSH_DIR}/known_hosts" + + if grep -qE '^@cert-authority .* fundwave_host_ca$' "${USER_SSH_DIR}/known_hosts"; then + sed -i.bak -E "s|^(@cert-authority .*) ssh-rsa .*|\1 ${HOST_CA_PUBKEY}|" "${USER_SSH_DIR}/known_hosts" + else + echo "@cert-authority * ${HOST_CA_PUBKEY}" >> "${USER_SSH_DIR}/known_hosts" + fi + echo "Host CA public key added to known_hosts." + else echo "Invalid Action" echo "Possible actions include:" echo " generateHostSSHCert: Generates SSH Certificate for Host" echo " generateClientSSHCert: Generates SSH Certificate for Client" + echo " getHostCAPublicKey: Gets the Host CA Public Key" exit 1; fi From edd67757653cf102a719b0be514f8e785f15e844 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 03:35:34 +0530 Subject: [PATCH 132/146] refactor [major]: update AWS-CLI script, rename to invoke-private-ca-aws-cli.sh --- .../client/generate-certificate-aws-cli.sh | 269 -------------- .../client/invoke-private-ca-aws-cli.sh | 342 ++++++++++++++++++ 2 files changed, 342 insertions(+), 269 deletions(-) delete mode 100755 private-ca/client/generate-certificate-aws-cli.sh create mode 100755 private-ca/client/invoke-private-ca-aws-cli.sh diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh deleted file mode 100755 index 65f9709..0000000 --- a/private-ca/client/generate-certificate-aws-cli.sh +++ /dev/null @@ -1,269 +0,0 @@ -#!/bin/bash - -trap 'rm -f *.json' EXIT - -CA_ACTION=${1:-"generateHostSSHCert"} -ENVIRONMENT=${2:-"client"} -AWS_PROFILE=${3:-"default"} -USER_SSH_DIR=${4:-"$HOME/.ssh"} -USER_AWS_DIR=${5:-"$HOME/.aws"} -SYSTEM_SSH_DIR=${6:-"/etc/ssh"} -LAMBDA_REGION=${7:-'us-west-2'} -CA_LAMBDA_FUNCTION_NAME=${8:-"privateCA"} -AWS_STS_REGION=${9:-"ap-southeast-1"} -AWS_EC2_REGION=${10:-"us-west-2"} - -PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) -[[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } - -is_mfa_enabled() { - grep -q 'get-credentials' ${USER_AWS_DIR}/credentials -} - -get_aws_credentials() { - local method=${1:-"host"} - local TEMP_CREDS - - if [[ $method == "host" ]]; then - TOKEN=$(curl -s --max-time 30 -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 120") - - if [[ -z "$TOKEN" ]]; then - echo "Failed to fetch EC2 metadata token. Are you running this script on an EC2 instance?" - exit 1 - fi - - INSTANCE_ROLE_NAME=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/) - TEMP_CREDS=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$INSTANCE_ROLE_NAME) - - elif [[ $method == "client" ]]; then - if is_mfa_enabled; then - CALLER_IDENTITY=$(aws sts get-caller-identity --profile $AWS_PROFILE) - [[ $? -ne 0 ]] && { echo "Your AWS credentials have either expired or are invalid. Please check your credentials and try again."; exit 1; } - - TEMP_CREDS=$(get-credentials $AWS_PROFILE) - else - TEMP_CREDS=$(aws sts get-session-token --profile $AWS_PROFILE | jq -r ".Credentials") - fi - AWS_PROFILE_ARG="--profile $AWS_PROFILE" - else - echo "Invalid environment provided. Allowed values are 'host' and 'client'"; exit 1; - fi - - 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 // .SessionToken // .Sessiontoken") -} - -# Check for options -while getopts ":h" option; do - case $option in - h) - echo "Usage: bash generate-certificate-aws-cli.sh [ACTION] [ENVIRONMENT] [AWS PROFILE] [USER SSH DIR] [USER AWS DIR] [SYSTEM SSH DIR] [AWS STS REGION]" - echo "" - echo "Actions:" - echo " generateHostSSHCert Generates SSH Certificate for Host" - echo " generateClientSSHCert Generates SSH Certificate for Client" - echo "" - echo "Parameters:" - echo " ENVIRONMENT Environment to use (default: client)" - echo " AWS PROFILE AWS profile to use (default: default)" - echo " USER SSH DIR Path to user's SSH directory (default: /home/$USER/.ssh)" - echo " USER AWS DIR Path to user's AWS directory (default: /home/$USER/.aws)" - echo " SYSTEM SSH DIR Path to system's SSH directory (default: /etc/ssh)" - echo " AWS STS REGION AWS region for STS operations (default: ap-southeast-1)" - exit;; - *) - echo "Error: Invalid option" - exit;; - esac -done - -# Check for CA Action -if [[ $CA_ACTION = "generateClientSSHCert" ]]; then - - [[ -d "${USER_SSH_DIR}" ]] || { echo "User SSH directory does not exist. Please provide the correct user SSH directory."; exit 1; } - - if test -f ${USER_SSH_DIR}/id_rsa-cert.pub; then - # Client SSH Certificate already exists - current_timestamp=$(TZ=UTC date -u +"%Y-%m-%dT%H:%M:%S") - certificate_expiration_timestamp=$(TZ=UTC 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 - # Host certificate generation is not allowed in client environment - if [[ $ENVIRONMENT = "client" ]]; then - echo -e "\nError: generateHostSSHCert is not allowed in client environment.\nHost certificate generation requires host/server environment.\n" - exit 1 - fi - if [ "$EUID" -ne 0 ]; then - echo "Run this script with sudo or as root for generating host certificate." - exit 1 - fi - - if test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub; then - # Host SSH Certificate already exists - current_timestamp=$(TZ=UTC date -u +"%Y-%m-%dT%H:%M:%S") - certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub | awk '/Valid:/{print $NF}') - - 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 - rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub - 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 | 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" - exit 1; -fi - -# Temporary Credentials -get_aws_credentials $ENVIRONMENT - -if [ ! -d "private-ca-client-env" ]; then - $PYTHON_EXEC -m venv private-ca-client-env -fi -source private-ca-client-env/bin/activate -pip install -q --upgrade --disable-pip-version-check boto3 - -# Update PYTHON_EXEC to use the Python executable from the activated virtual environment -# This ensures we use the venv's Python with the installed dependencies (boto3) -PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) - -# Auth Headers -output=$($PYTHON_EXEC aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_STS_REGION) || { - echo "Failed to generate auth header. Check aws-auth-header.py script."; - exit 1; -} -auth_header=$(echo "$output" | jq -er ".Authorization") || { - echo "Failed to parse Authorization from auth-header output."; - exit 1; -} -date=$(echo "$output" | jq -er ".Date") || { - echo "Failed to parse Date from auth-header output."; - exit 1; -} - - -INNER_JSON=$(jq -n \ - --arg amzDate "$date" \ - --arg authHeader "$auth_header" \ - --arg sessionToken "$SESSION_TOKEN" \ - --arg certPubkey "$CERT_PUBKEY" \ - --arg action "$CA_ACTION" \ - --arg awsRegion "$AWS_STS_REGION" \ - --arg awsEC2Region "$AWS_EC2_REGION" \ - '{ - auth: { - amzDate: $amzDate, - authorizationHeader: $authHeader, - sessionToken: $sessionToken - }, - certPubkey: $certPubkey, - action: $action, - awsSTSRegion: $awsRegion, - awsEC2Region: $awsEC2Region - }' | jq -c) - -# JSON with body as stringified JSON -json_body=$(jq -n --arg body "$INNER_JSON" '{body: $body}') -echo "$json_body" > event.json - -export AWS_PAGER="" - -INVOKE_OUTPUT=$(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 \ - $AWS_PROFILE_ARG 2>&1) || { - echo "$INVOKE_OUTPUT" - echo "Lambda invocation failed" - exit 1 -} - -response_body=$(cat response.json | jq -r ".body") || { - echo "Failed to parse response body."; - exit 1 -} -status_code=$(cat response.json | jq -r ".statusCode") || { - echo "Failed to parse status code."; - exit 1 -} - -if [[ $status_code -ne 200 ]]; then - echo "CA request failed (Status: ${status_code}): ${response_body}" - exit 1 -fi - -if [[ $CA_ACTION = "generateClientSSHCert" ]]; then - ENCODED_CERTIFICATE=$(echo "$response_body" | jq -er ".certificate") || { - echo "Certificate not found in Lambda response. Aborting."; - exit 1; - } - CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) - HOST_CA_PUBKEY=$(echo $response_body | 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" - - [[ -f "${USER_SSH_DIR}/known_hosts" ]] || touch "${USER_SSH_DIR}/known_hosts" - - # Add host CA public key to known_hosts file if it doesn't exist - if [[ $(grep -q "@cert-authority" "${USER_SSH_DIR}/known_hosts"; echo $?) -ne 0 ]]; then - # @cert-authority tells ssh to trust the host CA public key - # * means all hosts (wildcard) (you can also specify a list of comma separated hostnames) - # ${HOST_CA_PUBKEY} is the host CA public key that was used to sign the host certificate - echo "@cert-authority * ${HOST_CA_PUBKEY}" >> ${USER_SSH_DIR}/known_hosts - fi - -elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then - ENCODED_CERTIFICATE=$(echo "$response_body" | jq -er ".certificate") || { - echo "Certificate not found in Lambda response. Aborting."; - exit 1; - } - CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) - USER_CA_PUBKEY=$(echo $response_body | jq -r ".\"user_ca.pub\"" | base64 -d) - - if [[ -n "$CERTIFICATE" ]]; then - echo "$CERTIFICATE" > "${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" - echo "Certificate written to ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" - else - echo "Empty certificate received. Not writing to disk." - exit 1 - fi - - 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 -fi - -# Clean up -deactivate diff --git a/private-ca/client/invoke-private-ca-aws-cli.sh b/private-ca/client/invoke-private-ca-aws-cli.sh new file mode 100755 index 0000000..bae9945 --- /dev/null +++ b/private-ca/client/invoke-private-ca-aws-cli.sh @@ -0,0 +1,342 @@ +#!/bin/bash + +CA_ACTION=${1} +ENVIRONMENT=${2:-"client"} +USER_SSH_DIR=${3:-"$HOME/.ssh"} +USER_AWS_DIR=${4:-"$HOME/.aws"} +SYSTEM_SSH_DIR=${5:-"/etc/ssh"} +CA_LAMBDA_FUNCTION_NAME=${6:-"privateCA"} +LAMBDA_REGION=${7:-'us-west-2'} +AWS_STS_REGION=${8:-"eu-central-1"} +AWS_EC2_REGION=${9:-"eu-central-1"} + +PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) +[[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } + +trap 'clean_config_on_error' EXIT + +clean_config_on_error() { + rm -f private-ca-client-response.json private-ca-client-event.json + if [[ $? -ne 0 && $CA_ACTION == "generateHostSSHCert" ]]; then + if [[ "$CERT_VALID" == "true" ]]; then + echo "Keeping existing valid certificate." + else + echo "Error occurred. Cleaning host SSH config..." + sed -i "\|^HostCertificate ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config + sed -i "\|^TrustedUserCAKeys ${SYSTEM_SSH_DIR}/user_ca.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config + + rm ${SYSTEM_SSH_DIR}/user_ca.pub + rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub + systemctl restart sshd + echo "Host SSH config cleaned." + fi + fi +} + +get_aws_credentials() { + local TEMP_CREDS + + if [[ $ENVIRONMENT == "host" ]]; then + TOKEN=$(curl -s --max-time 30 -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 120") + + if [[ -z "$TOKEN" ]]; then + echo "Failed to fetch EC2 metadata token. Are you running this script on an EC2 instance?" + exit 1 + fi + + INSTANCE_ROLE_NAME=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/) + TEMP_CREDS=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$INSTANCE_ROLE_NAME) + + elif [[ $ENVIRONMENT == "client" ]]; then + if [[ -n "$AWS_ACCESS_KEY_ID" && -n "$AWS_SECRET_ACCESS_KEY" && -n "$AWS_SESSION_TOKEN" ]]; then + CALLER_IDENTITY=$(aws sts get-caller-identity) + [[ $? -ne 0 ]] && { echo "Your AWS credentials have either expired or are invalid. Please check your credentials and try again."; exit 1; } + TEMP_CREDS=$(echo "{\"AccessKeyId\":\"$AWS_ACCESS_KEY_ID\",\"SecretAccessKey\":\"$AWS_SECRET_ACCESS_KEY\",\"Token\":\"$AWS_SESSION_TOKEN\"}") + else + TEMP_CREDS=$(aws configure export-credentials 2>/dev/null) + SESSION_TOKEN=$(echo "$TEMP_CREDS" | jq -r '.Token // .SessionToken // .Sessiontoken // empty') + [[ -z "$SESSION_TOKEN" ]] && TEMP_CREDS=$(aws sts get-session-token | jq -r ".Credentials") + fi + else + echo "Invalid environment provided. Allowed values are 'host' and 'client'"; exit 1; + fi + + 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 // .SessionToken // .Sessiontoken") +} + +prepare_event_json() { + # Setup Python virtual environment + if [ ! -d "private-ca-client-env" ]; then + $PYTHON_EXEC -m venv private-ca-client-env + fi + source ./private-ca-client-env/bin/activate + pip install -q --upgrade --disable-pip-version-check boto3 + + # Update PYTHON_EXEC to use the Python executable from the activated virtual environment + # This ensures we use the venv's Python with the installed dependencies (boto3) + PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) + + # Generate Auth Headers + output=$($PYTHON_EXEC aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_STS_REGION) || { + echo "Failed to generate auth header. Check aws-auth-header.py script."; + exit 1; + } + auth_header=$(echo "$output" | jq -er ".Authorization") || { + echo "Failed to parse Authorization from auth-header output."; + exit 1; + } + date=$(echo "$output" | jq -er ".Date") || { + echo "Failed to parse Date from auth-header output."; + exit 1; + } + + INNER_JSON=$(jq -n \ + --arg amzDate "$date" \ + --arg authHeader "$auth_header" \ + --arg sessionToken "$SESSION_TOKEN" \ + --arg certPubkey "$CERT_PUBKEY" \ + --arg action "$CA_ACTION" \ + --arg awsRegion "$AWS_STS_REGION" \ + --arg awsEC2Region "$AWS_EC2_REGION" \ + '{ + auth: { + amzDate: $amzDate, + authorizationHeader: $authHeader, + sessionToken: $sessionToken + }, + certPubkey: $certPubkey, + action: $action, + awsSTSRegion: $awsRegion, + awsEC2Region: $awsEC2Region + }' | jq -c) + + # JSON with body as stringified JSON as required by server + json_body=$(jq -n --arg body "$INNER_JSON" '{body: $body}') + echo "$json_body" > private-ca-client-event.json +} + +invoke_lambda() { + INVOKE_OUTPUT=$(aws lambda invoke \ + --function-name ${CA_LAMBDA_FUNCTION_NAME} \ + --cli-binary-format raw-in-base64-out \ + --payload file://private-ca-client-event.json \ + private-ca-client-response.json \ + --region $LAMBDA_REGION \ + $AWS_PROFILE_ARG 2>&1) || { + echo "$INVOKE_OUTPUT" + echo "Lambda invocation failed" + exit 1 + } + + response_body=$(cat private-ca-client-response.json | jq -r ".body") || { + echo "Failed to parse response body."; + exit 1 + } + status_code=$(cat private-ca-client-response.json | jq -r ".statusCode") || { + echo "Failed to parse status code."; + exit 1 + } + + if [[ $status_code -ne 200 ]]; then + echo "CA request failed (Status: ${status_code}): ${response_body}" + exit 1 + fi + + # If the action is getHostCAPublicKey, we don't get a certificate + [[ $CA_ACTION == "getHostCAPublicKey" ]] && return + + ENCODED_CERTIFICATE=$(echo "$response_body" | jq -er ".certificate") || { + echo "Certificate not found in Lambda response. Aborting."; + exit 1; + } + + CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) + + if [[ -z "$CERTIFICATE" ]]; then + echo "Invalid certificate received. Aborting." + exit 1 + fi +} + +safe_replace_old_certificate() { + local CERTIFICATE=${1} + local CERT_FILE_PATH=${2} + + local TEMP_CERT_FILE="${CERT_FILE_PATH}.tmp" + + echo "$CERTIFICATE" > "$TEMP_CERT_FILE" + + # Verify the new certificate is valid + if ssh-keygen -Lf "$TEMP_CERT_FILE" >/dev/null 2>&1; then + mv "$TEMP_CERT_FILE" "${CERT_FILE_PATH}" + echo "New certificate written to ${CERT_FILE_PATH}" + CERT_VALID=true + else + rm -f "$TEMP_CERT_FILE" + echo "Generated certificate is invalid. Discarding." + exit 1 + fi +} + +# Check for options +while getopts ":h" option; do + case $option in + h) + echo "Usage: bash generate-certificate-aws-cli.sh [ACTION] [ENVIRONMENT] [AWS PROFILE] [USER SSH DIR] [USER AWS DIR] [SYSTEM SSH DIR] [AWS STS REGION]" + echo "" + echo "Actions:" + echo " generateHostSSHCert Generates SSH Certificate for Host" + echo " generateClientSSHCert Generates SSH Certificate for Client" + echo "" + echo "Parameters:" + echo " ENVIRONMENT Environment to use (default: client)" + echo " AWS PROFILE AWS profile to use (default: default)" + echo " USER SSH DIR Path to user's SSH directory (default: /home/$USER/.ssh)" + echo " USER AWS DIR Path to user's AWS directory (default: /home/$USER/.aws)" + echo " SYSTEM SSH DIR Path to system's SSH directory (default: /etc/ssh)" + echo " AWS STS REGION AWS region for STS operations (default: ap-southeast-1)" + exit;; + *) + echo "Error: Invalid option" + exit;; + esac +done + + +if [[ $CA_ACTION = "generateClientSSHCert" ]]; then + [[ -d "${USER_SSH_DIR}" ]] || { echo "User SSH directory does not exist. Please provide the correct user SSH directory."; exit 1; } + + # Check if a valid certificate exists + CERT_VALID=false + if test -f ${USER_SSH_DIR}/id_rsa-cert.pub; then + # Client SSH Certificate already exists + current_timestamp=$(date -u +%s) + certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${USER_SSH_DIR}/id_rsa-cert.pub 2>/dev/null | awk '/Valid:/{print $NF}') + + if [[ $certificate_expiration_timestamp > $current_timestamp ]]; then + # Certificate is valid + if [[ -f "${USER_SSH_DIR}/known_hosts" ]]; then + if grep -qE '^@cert-authority .* fundwave_host_ca$' "${USER_SSH_DIR}/known_hosts"; then + CERT_VALID=true + echo "A valid user certificate and known_hosts entry were found." + else + echo "User certificate is valid, but known_hosts entry is missing." + fi + else + echo "User certificate is valid, but known_hosts file is missing." + fi + else + echo "Existing user certificate is expired or invalid." + rm -f ${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 -N "" + [[ -f ${USER_SSH_DIR}/id_rsa-cert.pub ]] && rm ${USER_SSH_DIR}/id_rsa-cert.pub + } + CERT_PUBKEY=$(cat ${USER_SSH_DIR}/id_rsa.pub | base64 | tr -d \\n) + + get_aws_credentials + prepare_event_json + invoke_lambda + + HOST_CA_PUBKEY=$(echo $response_body | jq -r ".\"host_ca.pub\"" | base64 -d) + + safe_replace_old_certificate "$CERTIFICATE" "${USER_SSH_DIR}/id_rsa-cert.pub" + + [[ -f "${USER_SSH_DIR}/known_hosts" ]] || touch "${USER_SSH_DIR}/known_hosts" + + # Add host CA public key to known_hosts file if it doesn't exist and update it if it does + # @cert-authority tells ssh to trust the host CA public key + # ${HOST_CA_PUBKEY} is the host CA public key that was used to sign the host certificate + if grep -qE '^@cert-authority .* fundwave_host_ca$' "${USER_SSH_DIR}/known_hosts"; then + # Update existing line + sed -i.bak -E "s|^(@cert-authority .*) ssh-rsa .*|\1 ${HOST_CA_PUBKEY}|" "${USER_SSH_DIR}/known_hosts" + else + # * means all hosts (wildcard) (you can also specify a list of comma separated hostnames) + echo "@cert-authority * ${HOST_CA_PUBKEY}" >> "${USER_SSH_DIR}/known_hosts" + fi + +elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then + # Host certificate generation is not allowed in client environment + if [[ $ENVIRONMENT = "client" ]]; then + echo -e "\nError: generateHostSSHCert is not allowed in client environment.\nHost certificate generation requires host (server) environment.\n" + exit 1 + fi + if [ "$EUID" -ne 0 ]; then + echo "Run this script with sudo or as root for generating host certificate." + exit 1 + fi + + # Check if a valid certificate exists + CERT_VALID=false + half_life_seconds=259200 # 3 days + if test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub; then + current_timestamp=$(date -u +%s) + certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub 2>/dev/null | awk '/Valid:/{print $NF}') + [[ $(uname) == "Darwin" ]] && cert_expiry_epoch=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$certificate_expiration_timestamp" +"%s") || cert_expiry_epoch=$(date -d "$certificate_expiration_timestamp" +"%s") + next_run_timestamp=$((current_timestamp + half_life_seconds)) + + if [[ $cert_expiry_epoch -gt $next_run_timestamp ]]; then + CERT_VALID=true + echo "A valid host certificate was found at ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub." + else + echo "Existing host certificate will expire before next cron run." + rm -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub + 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 -N "" + [[ -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub ]] && rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub + } + CERT_PUBKEY=$(cat ${SYSTEM_SSH_DIR}/ssh_host_rsa_key.pub | base64 | tr -d \\n) + + get_aws_credentials + prepare_event_json + invoke_lambda + + USER_CA_PUBKEY=$(echo $response_body | jq -r ".\"user_ca.pub\"" | base64 -d) + + safe_replace_old_certificate "$CERTIFICATE" "${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" + + [[ -f "${SYSTEM_SSH_DIR}/user_ca.pub" ]] || touch "${SYSTEM_SSH_DIR}/user_ca.pub" + + if grep -qE '.* fundwave_user_ca$' "${SYSTEM_SSH_DIR}/user_ca.pub"; then + # Update existing line + sed -i.bak -E "s|ssh-rsa .* fundwave_user_ca$|${USER_CA_PUBKEY}|" "${SYSTEM_SSH_DIR}/user_ca.pub" + else + echo "${USER_CA_PUBKEY}" >> "${SYSTEM_SSH_DIR}/user_ca.pub" + fi + + 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 + systemctl restart sshd + +elif [[ $CA_ACTION = "getHostCAPublicKey" ]]; then + get_aws_credentials + prepare_event_json + invoke_lambda + + HOST_CA_PUBKEY=$(echo $response_body | jq -r ".\"host_ca.pub\"" | base64 -d) + + [[ -f "${USER_SSH_DIR}/known_hosts" ]] || touch "${USER_SSH_DIR}/known_hosts" + + if grep -qE '^@cert-authority .* fundwave_host_ca$' "${USER_SSH_DIR}/known_hosts"; then + sed -i.bak -E "s|^(@cert-authority .*) ssh-rsa .*|\1 ${HOST_CA_PUBKEY}|" "${USER_SSH_DIR}/known_hosts" + else + echo "@cert-authority * ${HOST_CA_PUBKEY}" >> "${USER_SSH_DIR}/known_hosts" + fi + echo "Host CA public key added to known_hosts." + +fi + +# Clean up +deactivate From 7632221425ae9c86546d653a37b515fdc9f716f7 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 03:51:33 +0530 Subject: [PATCH 133/146] refactor (private-ca-server): rename certificate generation functions and update validity period handling --- private-ca/client/invoke-private-ca-aws-cli.sh | 2 +- private-ca/server/src/generate-client-ssh-cert.ts | 10 +++++----- private-ca/server/src/generate-host-ssh-cert.ts | 6 +++--- private-ca/server/src/index_lambda.ts | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/private-ca/client/invoke-private-ca-aws-cli.sh b/private-ca/client/invoke-private-ca-aws-cli.sh index bae9945..781829e 100755 --- a/private-ca/client/invoke-private-ca-aws-cli.sh +++ b/private-ca/client/invoke-private-ca-aws-cli.sh @@ -6,7 +6,7 @@ USER_SSH_DIR=${3:-"$HOME/.ssh"} USER_AWS_DIR=${4:-"$HOME/.aws"} SYSTEM_SSH_DIR=${5:-"/etc/ssh"} CA_LAMBDA_FUNCTION_NAME=${6:-"privateCA"} -LAMBDA_REGION=${7:-'us-west-2'} +LAMBDA_REGION=${7:-'eu-central-1'} AWS_STS_REGION=${8:-"eu-central-1"} AWS_EC2_REGION=${9:-"eu-central-1"} diff --git a/private-ca/server/src/generate-client-ssh-cert.ts b/private-ca/server/src/generate-client-ssh-cert.ts index 1e556e8..f052544 100644 --- a/private-ca/server/src/generate-client-ssh-cert.ts +++ b/private-ca/server/src/generate-client-ssh-cert.ts @@ -5,13 +5,13 @@ import { format } from 'date-fns'; import { CallerIdentityResponse, SecretData } from './types/index.js'; const exec = util.promisify(child_process.exec); -const validityInDays = parseInt(process.env.validityInDays ?? '1', 10); +const clientCertValidityInDays = parseInt(process.env.clientCertValidityInDays ?? '1', 10); 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 ( +export const generateClientSSHCert = async ( callerIdentity: CallerIdentityResponse, secret: SecretData, certPubkey: string @@ -21,7 +21,7 @@ export const signClientSSHCertificate = async ( if (!match) { throw new Error(`Invalid ARN format: ${arn}`); } - const roleName = match[1]; + const principal = match[1]; const user_ca = Buffer.from(secret.user_ca, 'base64').toString('utf-8'); const decodedCertPubkey = Buffer.from(certPubkey, 'base64').toString('utf-8'); @@ -35,13 +35,13 @@ export const signClientSSHCertificate = async ( const now = new Date(); const validFrom = format(now, "yyyyMMddHHmmss"); - const validUntil = new Date(now.getTime() + (validityInDays * 24 * 60 * 60 * 1000)); + const validUntil = new Date(now.getTime() + (clientCertValidityInDays * 24 * 60 * 60 * 1000)); const validTo = format(validUntil, "yyyyMMddHHmmss"); const validityPeriod = `${validFrom}:${validTo}`; result = await exec( - `ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I client_${roleName} -n ${roleName} -V ${validityPeriod} ${publicKeyPath}` + `ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I client_${principal} -n ${principal} -V ${validityPeriod} ${publicKeyPath}` ); console.log('stdout:', result.stdout); diff --git a/private-ca/server/src/generate-host-ssh-cert.ts b/private-ca/server/src/generate-host-ssh-cert.ts index ada7763..b70afb3 100644 --- a/private-ca/server/src/generate-host-ssh-cert.ts +++ b/private-ca/server/src/generate-host-ssh-cert.ts @@ -6,9 +6,9 @@ import { format } from 'date-fns'; import { CallerIdentityResponse, SecretData } from './types/index.js'; const exec = util.promisify(child_process.exec); -const validityInDays = 7; +const hostCertValidityInDays = parseInt(process.env.hostCertValidityInDays ?? '7', 10); -export const signHostSSHCertificate = async ( +export const generateHostSSHCert = async ( callerIdentity: CallerIdentityResponse, secret: SecretData, certPubkey: string, @@ -40,7 +40,7 @@ export const signHostSSHCertificate = async ( const now = new Date(); const validFrom = format(now, "yyyyMMddHHmmss"); - const validUntil = new Date(now.getTime() + (60 * 1000)); + const validUntil = new Date(now.getTime() + (hostCertValidityInDays * 24 * 60 * 60 * 1000)); const validTo = format(validUntil, "yyyyMMddHHmmss"); const validityPeriod = `${validFrom}:${validTo}`; diff --git a/private-ca/server/src/index_lambda.ts b/private-ca/server/src/index_lambda.ts index b9cef60..763260b 100644 --- a/private-ca/server/src/index_lambda.ts +++ b/private-ca/server/src/index_lambda.ts @@ -1,5 +1,5 @@ -import { signHostSSHCertificate } from './generate-host-ssh-cert.js'; -import { signClientSSHCertificate } from './generate-client-ssh-cert.js'; +import { generateHostSSHCert } from './generate-host-ssh-cert.js'; +import { generateClientSSHCert } from './generate-client-ssh-cert.js'; import { getCallerIdentity } from './get-caller-identity.js'; import { getSecret } from './secret-manager-utils.js'; import { @@ -42,7 +42,7 @@ export const handler = async (event: any): Promise => { }; } - const hostSSHCert = await signHostSSHCertificate( + const hostSSHCert = await generateHostSSHCert( callerIdentity, secret, parsedEvent.certPubkey, @@ -65,7 +65,7 @@ export const handler = async (event: any): Promise => { body: JSON.stringify({ error: 'Missing certPubkey' }), }; } - const clientSSHCert = await signClientSSHCertificate( + const clientSSHCert = await generateClientSSHCert( callerIdentity, secret, parsedEvent.certPubkey From a46ae71d42e1c73b2d92139158a1b25fdebf06a4 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 10:21:03 +0530 Subject: [PATCH 134/146] refactor: add cert validity param and update half-life calculation --- private-ca/client/invoke-private-ca-aws-cli.sh | 9 +++++---- private-ca/client/invoke-private-ca.sh | 6 ++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/private-ca/client/invoke-private-ca-aws-cli.sh b/private-ca/client/invoke-private-ca-aws-cli.sh index 781829e..b8a3b49 100755 --- a/private-ca/client/invoke-private-ca-aws-cli.sh +++ b/private-ca/client/invoke-private-ca-aws-cli.sh @@ -9,6 +9,9 @@ CA_LAMBDA_FUNCTION_NAME=${6:-"privateCA"} LAMBDA_REGION=${7:-'eu-central-1'} AWS_STS_REGION=${8:-"eu-central-1"} AWS_EC2_REGION=${9:-"eu-central-1"} +CERT_VALIDITY_IN_DAYS=${10:-"6"} + +CERT_HALF_LIFE_SECONDS=$((CERT_VALIDITY_IN_DAYS * 24 * 60 * 60 / 2)) PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) [[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } @@ -123,8 +126,7 @@ invoke_lambda() { --cli-binary-format raw-in-base64-out \ --payload file://private-ca-client-event.json \ private-ca-client-response.json \ - --region $LAMBDA_REGION \ - $AWS_PROFILE_ARG 2>&1) || { + --region $LAMBDA_REGION) || { echo "$INVOKE_OUTPUT" echo "Lambda invocation failed" exit 1 @@ -272,12 +274,11 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then # Check if a valid certificate exists CERT_VALID=false - half_life_seconds=259200 # 3 days if test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub; then current_timestamp=$(date -u +%s) certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub 2>/dev/null | awk '/Valid:/{print $NF}') [[ $(uname) == "Darwin" ]] && cert_expiry_epoch=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$certificate_expiration_timestamp" +"%s") || cert_expiry_epoch=$(date -d "$certificate_expiration_timestamp" +"%s") - next_run_timestamp=$((current_timestamp + half_life_seconds)) + next_run_timestamp=$((current_timestamp + CERT_HALF_LIFE_SECONDS)) if [[ $cert_expiry_epoch -gt $next_run_timestamp ]]; then CERT_VALID=true diff --git a/private-ca/client/invoke-private-ca.sh b/private-ca/client/invoke-private-ca.sh index 5736377..97e5b55 100644 --- a/private-ca/client/invoke-private-ca.sh +++ b/private-ca/client/invoke-private-ca.sh @@ -8,6 +8,9 @@ USER_AWS_DIR=${5:-"$HOME/.aws"} SYSTEM_SSH_DIR=${6:-"/etc/ssh"} AWS_STS_REGION=${7:-"eu-central-1"} AWS_EC2_REGION=${8:-"eu-central-1"} +CERT_VALIDITY_IN_DAYS=${9:-"6"} + +CERT_HALF_LIFE_SECONDS=$((CERT_VALIDITY_IN_DAYS * 24 * 60 * 60 / 2)) PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) [[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } @@ -230,12 +233,11 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then # Check if a valid certificate exists CERT_VALID=false - half_life_seconds=259200 # 3 days if test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub; then current_timestamp=$(date -u +%s) certificate_expiration_timestamp=$(TZ=UTC ssh-keygen -Lf ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub 2>/dev/null | awk '/Valid:/{print $NF}') [[ $(uname) == "Darwin" ]] && cert_expiry_epoch=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$certificate_expiration_timestamp" +"%s") || cert_expiry_epoch=$(date -d "$certificate_expiration_timestamp" +"%s") - next_run_timestamp=$((current_timestamp + half_life_seconds)) + next_run_timestamp=$((current_timestamp + CERT_HALF_LIFE_SECONDS)) if [[ $cert_expiry_epoch -gt $next_run_timestamp ]]; then CERT_VALID=true From 418bbbdf28f7aa7188242726df4d36737d93533d Mon Sep 17 00:00:00 2001 From: Parus Giri <71807564+giriparus@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:06:36 +0530 Subject: [PATCH 135/146] Update private-ca/server/src/get-public-ip-address.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../server/src/get-public-ip-address.ts | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/private-ca/server/src/get-public-ip-address.ts b/private-ca/server/src/get-public-ip-address.ts index 0fc1551..5c19f1b 100644 --- a/private-ca/server/src/get-public-ip-address.ts +++ b/private-ca/server/src/get-public-ip-address.ts @@ -10,21 +10,17 @@ export const getPublicIpAddress = async ( InstanceIds: [instanceId], }); - try { - const response = await client.send(command); - - const instance = response.Reservations?.[0]?.Instances?.[0]; + const response = await client.send(command); + + const instance = response.Reservations?.[0]?.Instances?.[0]; - if (!instance) { - throw new Error(`Instance ${instanceId} not found`); - } - - if (!instance.PublicIpAddress) { - throw new Error(`Instance ${instanceId} does not have a public IP address`); - } + if (!instance) { + throw new Error(`Instance ${instanceId} not found`); + } - return instance.PublicIpAddress; - } catch (error) { - throw error; + if (!instance.PublicIpAddress) { + throw new Error(`Instance ${instanceId} does not have a public IP address`); } + + return instance.PublicIpAddress; }; \ No newline at end of file From ff0d72bc7f84be8648afed3ed4fcf717a9b5fe65 Mon Sep 17 00:00:00 2001 From: Parus Giri <71807564+giriparus@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:07:20 +0530 Subject: [PATCH 136/146] Update private-ca/server/src/secret-manager-utils.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- private-ca/server/src/secret-manager-utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/private-ca/server/src/secret-manager-utils.ts b/private-ca/server/src/secret-manager-utils.ts index e21a322..0345809 100644 --- a/private-ca/server/src/secret-manager-utils.ts +++ b/private-ca/server/src/secret-manager-utils.ts @@ -18,7 +18,7 @@ export const getSecret = async ( const secret = JSON.parse(response.SecretString) as SecretData; return secret; } catch (err) { - console.log(err); + console.error(`Failed to retrieve secret for account ${accountId} from AWS Secrets Manager:`, err); return null; } }; \ No newline at end of file From b2cb20fcd2436c9206dd89a4f145314f5209aeba Mon Sep 17 00:00:00 2001 From: Parus Giri <71807564+giriparus@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:07:38 +0530 Subject: [PATCH 137/146] Update private-ca/server/src/index_lambda.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- private-ca/server/src/index_lambda.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/private-ca/server/src/index_lambda.ts b/private-ca/server/src/index_lambda.ts index 763260b..c2d0e57 100644 --- a/private-ca/server/src/index_lambda.ts +++ b/private-ca/server/src/index_lambda.ts @@ -88,7 +88,7 @@ export const handler = async (event: any): Promise => { } default: - console.log("Invalid Action"); + console.log(`Invalid Action: ${parsedEvent.action}`, { event: parsedEvent }); return { statusCode: 400, body: JSON.stringify({ error: 'Invalid Action' }), From faaffea6dbf1b10f2d9f65ed5054014987f068d1 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 11:20:38 +0530 Subject: [PATCH 138/146] refactor: add cert half-life param with default value as 3 days, remove cert validity in days param --- private-ca/client/invoke-private-ca-aws-cli.sh | 4 +--- private-ca/client/invoke-private-ca.sh | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/private-ca/client/invoke-private-ca-aws-cli.sh b/private-ca/client/invoke-private-ca-aws-cli.sh index b8a3b49..a5a6d2f 100755 --- a/private-ca/client/invoke-private-ca-aws-cli.sh +++ b/private-ca/client/invoke-private-ca-aws-cli.sh @@ -9,9 +9,7 @@ CA_LAMBDA_FUNCTION_NAME=${6:-"privateCA"} LAMBDA_REGION=${7:-'eu-central-1'} AWS_STS_REGION=${8:-"eu-central-1"} AWS_EC2_REGION=${9:-"eu-central-1"} -CERT_VALIDITY_IN_DAYS=${10:-"6"} - -CERT_HALF_LIFE_SECONDS=$((CERT_VALIDITY_IN_DAYS * 24 * 60 * 60 / 2)) +CERT_HALF_LIFE_SECONDS=${10:-$((6 * 24 * 60 * 60 / 2))} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) [[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } diff --git a/private-ca/client/invoke-private-ca.sh b/private-ca/client/invoke-private-ca.sh index 97e5b55..3405281 100644 --- a/private-ca/client/invoke-private-ca.sh +++ b/private-ca/client/invoke-private-ca.sh @@ -8,9 +8,7 @@ USER_AWS_DIR=${5:-"$HOME/.aws"} SYSTEM_SSH_DIR=${6:-"/etc/ssh"} AWS_STS_REGION=${7:-"eu-central-1"} AWS_EC2_REGION=${8:-"eu-central-1"} -CERT_VALIDITY_IN_DAYS=${9:-"6"} - -CERT_HALF_LIFE_SECONDS=$((CERT_VALIDITY_IN_DAYS * 24 * 60 * 60 / 2)) +CERT_HALF_LIFE_SECONDS=${9:-$((6 * 24 * 60 * 60 / 2))} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) [[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } From 566f230365173112bfe036412f5450d8f25101c4 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 11:44:09 +0530 Subject: [PATCH 139/146] fix: update script name in Dockerfile --- private-ca/client/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/private-ca/client/Dockerfile b/private-ca/client/Dockerfile index 15b5459..ea476de 100644 --- a/private-ca/client/Dockerfile +++ b/private-ca/client/Dockerfile @@ -4,9 +4,9 @@ RUN apk add bash curl jq python3 openssh WORKDIR /app -COPY generate-certificate-curl.sh . +COPY invoke-private-ca.sh . COPY aws-auth-header.py . -RUN chmod +x generate-certificate-curl.sh +RUN chmod +x invoke-private-ca.sh -ENTRYPOINT ["bash", "generate-certificate-curl.sh"] \ No newline at end of file +ENTRYPOINT ["bash", "invoke-private-ca.sh"] \ No newline at end of file From 08e9d2ee88b9c22d6ad7b6eb200eab3a1f7e9858 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 11:44:17 +0530 Subject: [PATCH 140/146] update README --- private-ca/README.md | 61 +++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/private-ca/README.md b/private-ca/README.md index ce58b44..a189124 100644 --- a/private-ca/README.md +++ b/private-ca/README.md @@ -43,13 +43,19 @@ This creates the following resources on AWS: #### For client certificates: ```bash -bash client/generate-certificate-curl.sh generateClientSSHCert client +bash invoke-private-ca.sh generateClientSSHCert client ``` #### For host certificates: ```bash -bash client/generate-certificate-curl.sh generateHostSSHCert host +bash invoke-private-ca.sh generateHostSSHCert host +``` + +#### For getting host CA public key: + +```bash +bash invoke-private-ca.sh getHostCAPublicKey client ``` **Note:** @@ -59,12 +65,12 @@ bash client/generate-certificate-curl.sh generateHostSSHCert ho ### Running via AWS CLI (Lambda) -The `generate-certificate-aws-cli.sh` script provides an alternative approach to generate certificates. This method uses AWS CLI to invoke a Lambda function rather than making direct HTTP requests. +The `invoke-private-ca-aws-cli.sh` script provides an alternative approach to generate certificates. This method uses AWS CLI to invoke the Lambda function rather than making HTTP requests. #### Usage: ```bash -bash client/generate-certificate-aws-cli.sh +bash invoke-private-ca-aws-cli.sh ``` ### Running via Docker @@ -80,15 +86,12 @@ bash client/generate-certificate-aws-cli.sh / \ host \ - default \ - /root/.ssh ``` ## Running as a cron job (optional) @@ -101,7 +104,7 @@ Sample script: #!/bin/bash # Create the cron job entry -echo "* */1 * * * cd /path/to/private-ca && bash client/generate-certificate-curl.sh generateHostSSHCert https:/// host >> /home/cron.log 2>&1" > /tmp/root_crontab +echo "* */1 * * * cd /path/to/private-ca/client && bash invoke-private-ca.sh generateHostSSHCert https:/// host >> /home/cron.log 2>&1" > /tmp/root_crontab # Load into root's crontab crontab -u root /tmp/root_crontab @@ -112,25 +115,27 @@ systemctl start cron 2>/dev/null || systemctl start crond 2>/dev/null ## Script Parameters -Both `generate-certificate-curl.sh` and `generate-certificate-aws-cli.sh` accept several shared and some script-specific parameters. - -| Parameter | Required | Description | Used In Script(s) | Default Value | -| ------------------------- | -------- | ------------------------------------------------------------------------------ | --------------------------------- | ------------------ | -| `CA_ACTION` | Yes | Action to perform: `generateClientSSHCert` or `generateHostSSHCert` | Both | — | -| `CA_URL` | Yes | URL of the Private CA | `generate-certificate-curl.sh` | — | -| `ENVIRONMENT` | No | Machine environment: `"client"` (uses AWS CLI) or `"host"` (uses EC2 metadata) | Both | `client` | -| `AWS_PROFILE` | No | AWS CLI profile name | Both | `default` | -| `USER_SSH_DIR` | No | Path to user's SSH directory | Both | `/home/$USER/.ssh` | -| `USER_AWS_DIR` | No | Path to user's AWS directory | `generate-certificate-curl.sh` | `/home/$USER/.aws` | -| `SYSTEM_SSH_DIR` | No | Path to system SSH directory | Both | `/etc/ssh` | -| `AWS_STS_REGION` | No | AWS region to use for STS operations | Both | `ap-southeast-1` | -| `LAMBDA_REGION` | No | AWS region where the Lambda function is deployed | `generate-certificate-aws-cli.sh` | `us-west-2` | -| `CA_LAMBDA_FUNCTION_NAME` | No | Name of the Lambda function that performs certificate signing | `generate-certificate-aws-cli.sh` | `privateCA` | +Both `invoke-private-ca.sh` and `invoke-private-ca-aws-cli.sh` accept several shared and some script-specific parameters. + +| Parameter | Required | Description | Used In Script(s) | Default Value | +| ------------------------- | -------- | ------------------------------------------------------------------------------------------ | ------------------------------ | ------------------------- | +| `CA_ACTION` | Yes | Action to perform: `generateClientSSHCert`, `generateHostSSHCert`, or `getHostCAPublicKey` | Both | — | +| `CA_URL` | Yes | URL of the Private CA | `invoke-private-ca.sh` | — | +| `ENVIRONMENT` | No | Machine environment: `"client"` (uses AWS CLI) or `"host"` (uses EC2 metadata) | Both | `client` | +| `USER_SSH_DIR` | No | Path to user's SSH directory | Both | `$HOME/.ssh` | +| `USER_AWS_DIR` | No | Path to user's AWS directory | `invoke-private-ca.sh` | `$HOME/.aws` | +| `SYSTEM_SSH_DIR` | No | Path to system SSH directory | Both | `/etc/ssh` | +| `AWS_STS_REGION` | No | AWS region to use for STS operations | Both | `eu-central-1` | +| `LAMBDA_REGION` | No | AWS region where the Lambda function is deployed | `invoke-private-ca-aws-cli.sh` | `eu-central-1` | +| `CA_LAMBDA_FUNCTION_NAME` | No | Name of the Lambda function that performs certificate signing | `invoke-private-ca-aws-cli.sh` | `privateCA` | +| `AWS_EC2_REGION` | No | AWS region where the EC2 instance is deployed | `invoke-private-ca-aws-cli.sh` | `eu-central-1` | +| `CERT_HALF_LIFE_SECONDS` | No | Certificate half-life in seconds | Both | `259200 seconds (3 days)` | ## Important Notes -- **Certificate Type**: Determined by the `CA_ACTION` parameter (`generateClientSSHCert` or `generateHostSSHCert`) +- **Certificate Type**: Determined by the `CA_ACTION` parameter (`generateClientSSHCert`, `generateHostSSHCert`, or `getHostCAPublicKey`) - **Permissions**: Host certificates require sudo privileges for system directory access +- **Public Key Retrieval**: The `getHostCAPublicKey` action retrieves the Host CA's public key for host certificate verification ## Client Environment Limitations @@ -144,8 +149,12 @@ Both `generate-certificate-curl.sh` and `generate-certificate-aws-cli.sh` accept - `deploy-server-on-lambda.sh`: Script to deploy the Lambda function and related AWS resources - `update-server-on-lambda.sh`: Script to update the deployed Lambda function - `client/`: Directory containing client-side tools - - `generate-certificate-curl.sh`: Main script for certificate generation using curl - - `generate-certificate-aws-cli.sh`: Alternative script using AWS CLI + - `invoke-private-ca.sh`: Main script for certificate generation using curl + - `invoke-private-ca-aws-cli.sh`: Alternative script using AWS CLI - `aws-auth-header.py`: Python helper for generating AWS authentication headers - `Dockerfile`: Docker container configuration - `server/`: Directory containing server-side Lambda function code + +``` + +``` From 1fb84fda50236e5660dd291579f693ecf8b89d28 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 12:03:39 +0530 Subject: [PATCH 141/146] refactor: reorder EC2-region param --- private-ca/client/invoke-private-ca-aws-cli.sh | 15 ++++++++------- private-ca/client/invoke-private-ca.sh | 10 +++++----- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/private-ca/client/invoke-private-ca-aws-cli.sh b/private-ca/client/invoke-private-ca-aws-cli.sh index a5a6d2f..08564a1 100755 --- a/private-ca/client/invoke-private-ca-aws-cli.sh +++ b/private-ca/client/invoke-private-ca-aws-cli.sh @@ -2,13 +2,14 @@ CA_ACTION=${1} ENVIRONMENT=${2:-"client"} -USER_SSH_DIR=${3:-"$HOME/.ssh"} -USER_AWS_DIR=${4:-"$HOME/.aws"} -SYSTEM_SSH_DIR=${5:-"/etc/ssh"} -CA_LAMBDA_FUNCTION_NAME=${6:-"privateCA"} -LAMBDA_REGION=${7:-'eu-central-1'} -AWS_STS_REGION=${8:-"eu-central-1"} -AWS_EC2_REGION=${9:-"eu-central-1"} +AWS_EC2_REGION=${3:-"eu-central-1"} +USER_SSH_DIR=${4:-"$HOME/.ssh"} +USER_AWS_DIR=${5:-"$HOME/.aws"} +SYSTEM_SSH_DIR=${6:-"/etc/ssh"} +CA_LAMBDA_FUNCTION_NAME=${7:-"privateCA"} +LAMBDA_REGION=${8:-'eu-central-1'} +AWS_STS_REGION=${9:-"eu-central-1"} + CERT_HALF_LIFE_SECONDS=${10:-$((6 * 24 * 60 * 60 / 2))} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) diff --git a/private-ca/client/invoke-private-ca.sh b/private-ca/client/invoke-private-ca.sh index 3405281..542e843 100644 --- a/private-ca/client/invoke-private-ca.sh +++ b/private-ca/client/invoke-private-ca.sh @@ -3,11 +3,11 @@ CA_ACTION=${1} CA_URL=${2} ENVIRONMENT=${3:-"client"} -USER_SSH_DIR=${4:-"$HOME/.ssh"} -USER_AWS_DIR=${5:-"$HOME/.aws"} -SYSTEM_SSH_DIR=${6:-"/etc/ssh"} -AWS_STS_REGION=${7:-"eu-central-1"} -AWS_EC2_REGION=${8:-"eu-central-1"} +AWS_EC2_REGION=${4:-"eu-central-1"} +USER_SSH_DIR=${5:-"$HOME/.ssh"} +USER_AWS_DIR=${6:-"$HOME/.aws"} +SYSTEM_SSH_DIR=${7:-"/etc/ssh"} +AWS_STS_REGION=${8:-"eu-central-1"} CERT_HALF_LIFE_SECONDS=${9:-$((6 * 24 * 60 * 60 / 2))} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) From b0cb5c496f33e788d5e18378393b0b334eaeb19a Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 13:57:58 +0530 Subject: [PATCH 142/146] refactor: simplify cert half life calculation --- private-ca/client/invoke-private-ca-aws-cli.sh | 2 +- private-ca/client/invoke-private-ca.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/private-ca/client/invoke-private-ca-aws-cli.sh b/private-ca/client/invoke-private-ca-aws-cli.sh index 08564a1..e44a07b 100755 --- a/private-ca/client/invoke-private-ca-aws-cli.sh +++ b/private-ca/client/invoke-private-ca-aws-cli.sh @@ -10,7 +10,7 @@ CA_LAMBDA_FUNCTION_NAME=${7:-"privateCA"} LAMBDA_REGION=${8:-'eu-central-1'} AWS_STS_REGION=${9:-"eu-central-1"} -CERT_HALF_LIFE_SECONDS=${10:-$((6 * 24 * 60 * 60 / 2))} +CERT_HALF_LIFE_SECONDS=${10:-$((3 * 24 * 60 * 60))} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) [[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } diff --git a/private-ca/client/invoke-private-ca.sh b/private-ca/client/invoke-private-ca.sh index 542e843..a5e2735 100644 --- a/private-ca/client/invoke-private-ca.sh +++ b/private-ca/client/invoke-private-ca.sh @@ -8,7 +8,7 @@ USER_SSH_DIR=${5:-"$HOME/.ssh"} USER_AWS_DIR=${6:-"$HOME/.aws"} SYSTEM_SSH_DIR=${7:-"/etc/ssh"} AWS_STS_REGION=${8:-"eu-central-1"} -CERT_HALF_LIFE_SECONDS=${9:-$((6 * 24 * 60 * 60 / 2))} +CERT_HALF_LIFE_SECONDS=${9:-$((3 * 24 * 60 * 60))} PYTHON_EXEC=$(which python 2>/dev/null || which python3 2>/dev/null) [[ $? -ne 0 ]] && { echo "Python binary not found."; exit 1; } From a9472fdc8b6789db765e6573f44189fea278301e Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 14:00:13 +0530 Subject: [PATCH 143/146] fix: do not delete cert and user_ca files if cert is invalid, only clean sshd_config --- private-ca/client/invoke-private-ca-aws-cli.sh | 4 +--- private-ca/client/invoke-private-ca.sh | 6 ++---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/private-ca/client/invoke-private-ca-aws-cli.sh b/private-ca/client/invoke-private-ca-aws-cli.sh index e44a07b..489f1b9 100755 --- a/private-ca/client/invoke-private-ca-aws-cli.sh +++ b/private-ca/client/invoke-private-ca-aws-cli.sh @@ -23,12 +23,10 @@ clean_config_on_error() { if [[ "$CERT_VALID" == "true" ]]; then echo "Keeping existing valid certificate." else - echo "Error occurred. Cleaning host SSH config..." + echo "Certificate generation failed and current cert would expire before next run. Cleaning host SSH config..." sed -i "\|^HostCertificate ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config sed -i "\|^TrustedUserCAKeys ${SYSTEM_SSH_DIR}/user_ca.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config - rm ${SYSTEM_SSH_DIR}/user_ca.pub - rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub systemctl restart sshd echo "Host SSH config cleaned." fi diff --git a/private-ca/client/invoke-private-ca.sh b/private-ca/client/invoke-private-ca.sh index a5e2735..2529277 100644 --- a/private-ca/client/invoke-private-ca.sh +++ b/private-ca/client/invoke-private-ca.sh @@ -20,12 +20,10 @@ clean_config_on_error() { if [[ "$CERT_VALID" == "true" ]]; then echo "Keeping existing valid certificate." else - echo "Error occurred. Cleaning host SSH config..." + echo "Certificate generation failed and current cert would expire before next run. Cleaning host SSH config..." sed -i "\|^HostCertificate ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config sed -i "\|^TrustedUserCAKeys ${SYSTEM_SSH_DIR}/user_ca.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config - - rm ${SYSTEM_SSH_DIR}/user_ca.pub - rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub + systemctl restart sshd echo "Host SSH config cleaned." fi From 750be8311351fb745adf829d414fe5af1e60e1d8 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 14:06:24 +0530 Subject: [PATCH 144/146] update instructions in invoke-private-ca scripts to include new parameters --- private-ca/client/invoke-private-ca-aws-cli.sh | 3 ++- private-ca/client/invoke-private-ca.sh | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/private-ca/client/invoke-private-ca-aws-cli.sh b/private-ca/client/invoke-private-ca-aws-cli.sh index 489f1b9..c4d704f 100755 --- a/private-ca/client/invoke-private-ca-aws-cli.sh +++ b/private-ca/client/invoke-private-ca-aws-cli.sh @@ -183,11 +183,12 @@ safe_replace_old_certificate() { while getopts ":h" option; do case $option in h) - echo "Usage: bash generate-certificate-aws-cli.sh [ACTION] [ENVIRONMENT] [AWS PROFILE] [USER SSH DIR] [USER AWS DIR] [SYSTEM SSH DIR] [AWS STS REGION]" + echo "Usage: bash invoke-private-ca-aws-cli.sh [ACTION] [ENVIRONMENT] [AWS_EC2_REGION] [AWS PROFILE] [USER SSH DIR] [USER AWS DIR] [SYSTEM SSH DIR] [CA_LAMBDA_FUNCTION_NAME] [LAMBDA_REGION] [AWS STS REGION] [CERT HALF LIFE SECONDS]" echo "" echo "Actions:" echo " generateHostSSHCert Generates SSH Certificate for Host" echo " generateClientSSHCert Generates SSH Certificate for Client" + echo " getHostCAPublicKey Gets Host CA Public Key" echo "" echo "Parameters:" echo " ENVIRONMENT Environment to use (default: client)" diff --git a/private-ca/client/invoke-private-ca.sh b/private-ca/client/invoke-private-ca.sh index 2529277..5849a4f 100644 --- a/private-ca/client/invoke-private-ca.sh +++ b/private-ca/client/invoke-private-ca.sh @@ -23,7 +23,7 @@ clean_config_on_error() { echo "Certificate generation failed and current cert would expire before next run. Cleaning host SSH config..." sed -i "\|^HostCertificate ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config sed -i "\|^TrustedUserCAKeys ${SYSTEM_SSH_DIR}/user_ca.pub\$|d" ${SYSTEM_SSH_DIR}/sshd_config - + systemctl restart sshd echo "Host SSH config cleaned." fi @@ -149,10 +149,11 @@ safe_replace_old_certificate() { while getopts ":h" option; do case $option in h) - echo "Usage: bash generate-certificate-curl.sh [ACTION] [CA URL] [ENVIRONMENT] [USER SSH DIR] [USER AWS DIR] [SYSTEM SSH DIR] [AWS STS REGION] [AWS EC2 REGION]" + echo "Usage: bash invoke-private-ca.sh [ACTION] [CA URL] [ENVIRONMENT] [AWS EC2 REGION] [USER SSH DIR] [USER AWS DIR] [SYSTEM SSH DIR] [AWS STS REGION] [CERT HALF LIFE SECONDS]" echo "Possible actions:" echo " generateHostSSHCert: Generates SSH Certificate for Host" echo " generateClientSSHCert: Generates SSH Certificate for Client" + echo " getHostCAPublicKey: Gets the Host CA Public Key" exit;; *) echo "Error: Invalid option" From f4c4d0cc64af353bfdcb5d6234c2ac6825f25029 Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 14:15:16 +0530 Subject: [PATCH 145/146] fix: read AWS_REGION from vars instead of secrets in update-private-ca-lambda workflow --- .github/workflows/update-private-ca-lambda.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-private-ca-lambda.yml b/.github/workflows/update-private-ca-lambda.yml index 9096267..17cdc2b 100644 --- a/.github/workflows/update-private-ca-lambda.yml +++ b/.github/workflows/update-private-ca-lambda.yml @@ -23,7 +23,7 @@ jobs: - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: - aws-region: ${{ secrets.AWS_REGION }} + aws-region: ${{ vars.AWS_REGION }} role-to-assume: ${{ secrets.AWS_PRIVATE_CA_LAMBDA_UPDATE_ROLE }} role-session-name: UpdatePrivateCALambda From 2fe5da1367ac2e12bfe669ca0bde78b6e227397a Mon Sep 17 00:00:00 2001 From: Harshit Rai Verma Date: Fri, 25 Jul 2025 14:51:34 +0530 Subject: [PATCH 146/146] chore: add environment specification for update-private-ca-lambda workflow --- .github/workflows/update-private-ca-lambda.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/update-private-ca-lambda.yml b/.github/workflows/update-private-ca-lambda.yml index 17cdc2b..8376be2 100644 --- a/.github/workflows/update-private-ca-lambda.yml +++ b/.github/workflows/update-private-ca-lambda.yml @@ -15,6 +15,7 @@ permissions: jobs: update-lambda: runs-on: "ubuntu-24.04" + environment: "Prod" steps: - name: Checkout code