Search

NestJS AWS Parameter Store 라이브러리 개발 일지

주제
Backend
날짜
2023/04/22

AWS Parameter Store 란?

AWS Systems Manager의 기능인 Parameter Store는 구성 데이터 관리 및 암호 관리를 위한 안전한 계층적 스토리지를 제공합니다.
AWS Docs

특징

구성 데이터 관리 및 암호 관리를 위한 안전한 계층적 스토리지를 제공한다.
파라미터를 생성할 때 고유 이름을 지정해서 다양한 곳에서 참조 가능하다.
참조 가능한 곳 : 스크립트, 명령, SSM 문서, 구성 및 자동화 워크플로
파라미터 값으로 저장할 수 있는 데이터.
암호
데이터베이스 문자열
Amazon Machine Image(AMI) ID
라이선스 코드
저장 값 종류.
문자열 (String)
문자열 목록 (StringList)
암호화된 데이터 (SecureString)
암호 교체 수명 주기를 구현하려면 AWS Secrets Manager를 사용합니다. Secrets Manager를 사용하면 수명 주기 동안 데이터베이스 자격 증명, API 키 및 기타 보안 암호를 손쉽게 교체, 관리 및 검색할 수 있습니다.

사용 비용

무료
Parameter Store는 추가 비용 없이 제공됩니다.

Parameter Store 파라미터를 지원하는 AWS 서비스

Amazon EC2
Amazon Elastic Container Service
AWS Lambda
AWS CloudFormation
AWS CodeBuild
AWS CodeDeploy

요약

계정에 종속된 단일 Managed key-value Store
계정 전체에서 공유하는 단일 저장소
AWS에서 직접 관리해준다
무료로 제공

Parameter Store가 주는 이점

안전하고 확장 가능한 호스팅 방식 암호 관리 서비스를 사용한다. 즉, 관리할 서버가 없다.
데이터를 코드와 격리하여 보안 태세를 개선한다.
구성 데이터 및 암호화된 문자열을 계층으로 저장하고 버전을 추적한다.
세분화된 수준에서 액세스를 제어하고 감독 및 검사한다.
Parameter Store는 AWS 리전의 여러 가용 영역에서 호스팅되기 때문에 파라미터를 안정적으로 저장한다.

Parameter Store 기능

변경 알림
파라미터 및 파라미터 정책 모두에 대해 변경 알림을 구성하고 자동화된 작업을 호출할 수 있다.
파라미터 구성
파라미터에 태그를 지정하면 파라미터에 지정한 태그를 토대로 하나 이상의 파라미터를 개별적으로 식별할 수 있다.
예) 특정 환경이나 부서에 대한 파라미터에 태그를 지정할 수 있다.
레이블 버전
레이블을 생성하여 파라미터 버전에 대한 별칭을 연결할 수 있다.
레이블이 있으면 파라미터 버전이 여러 개일 때 버전의 용도를 기억하기 쉽다.
데이터 유효성 검사
Amazon EC2 인스턴스를 가리키는 파라미터를 생성 가능.
Parameter Store는 이러한 파라미터를 검증하여 예상 리소스 유형을 참조하는지, 리소스가 존재하는지, 고객에게 리소스 사용 권한이 있는지 확인한다.
예) aws:ec2:image 데이터 형식의 값으로 AMI ID가 있는 파라미터를 생성할 수 있다. Parameter Store은 파라미터 값이 AMI ID에 대한 형식 지정 요구 사항을 충족하고 지정된 AMI가 AWS 계정에서 사용 가능한지 확인하기 위해 비동기 검증 작업을 수행한다.
참조 암호
이미 Parameter Store 파라미터 참조를 지원하는 다른 AWS 서비스을 사용할 때 Secrets Manager 암호를 검색할 수 있도록 Parameter Store을 AWS Secrets Manager와 통합했다.
다른 AWS 서비스에서 액세스 가능
Parameter Store 파라미터와 기타 Systems Manager 기능 및 AWS 서비스을 사용하여 중앙 Store에서 암호와 구성 데이터를 검색할 수 있다.
파라미터는 Run Command, Automation, State Manager, AWS Systems Manager의 기능과 같은 Systems Manager 기능과 함께 사용할 수 있다.
또한 다음을 비롯한 다양한 다른 AWS 서비스에서도 파라미터를 참조할 수 있다.
Amazon EC2
Amazon ECS
AWS Secrets Manager
AWS Lambda
AWS CloudFormation
AWS CodeBuild
AWS CodePipeline
AWS CodeDeploy

기존 라이브러리들의 부족한점

NestJS와 Parameter Store를 키워드로 NPM에 검색했을 때 3개가 검색되었다.
첫 번째는 내 라이브러리이다.
2번째 라이브러리인 nestjs-ssmnestjs-param-store를 단순 fork한 것이다.

param-store-service

가장 많은 다운로드 수를 보여준 라이브러리이다.
하지만 해당 라이브러리의 경우 accessKey를 설정할 수 없다는 단점이 있다.
모듈을 등록할 때의 쓰이는 옵션의 interface 코드를 보면 region과 param store path만 설정할 수 있는 것을 확인할 수 있다.
이는 Local에 설정되어 있는 aws configure를 기반으로 작동된다는 것을 유추할 수 있다.
만약에 Local이 아닌 별도의 AWS에 접근하고 싶을 경우 해당 라이브러리로는 불가능 하다.
export interface ModuleOptions { awsRegion: string; awsParamSorePath: string; }
TypeScript
복사

nestjs-param-store

두 번째로 많은 다운로드 수를 보여준 라이브러리이다.
하지만 해당 라이브러리의 getParametersByPath에서 모든 파라미터를 가져올 수 없다는 단점이 있다.
AWS Parameter Store는 일종의 Pagination 기능을 제공한다.
Retrieve information about one or more parameters in a specific hierarchy.
Request results are returned on a best-effort basis. If you specify MaxResults in the request, the response includes information up to the limit specified. The number of items returned, however, can be between zero and the value of MaxResults. If the service reaches an internal limit while processing the results, it stops the operation and returns the matching values up to that point and a NextToken. You can specify the NextToken in a subsequent call to get the next set of results.
AWS Parameter Store의 Request Syntax이다.
{ "MaxResults": number, "NextToken": "string", "ParameterFilters": [ { "Key": "string", "Option": "string", "Values": [ "string" ] } ], "Path": "string", "Recursive": boolean, "WithDecryption": boolean }
TypeScript
복사
MaxResult는 각 페이지에서 보여줄 최대 파라미터 갯수, NextToken은 다음 페이지를 뜻한다.
이 때문에 파라미터가 많을 경우 NextToken을 검사하면서 모든 파라미터를 가져와야한다.
하지만 해당 라이브러리의 경우 이와 같은 처리가 되어 있지 않기 때문에 파라미터가 많아질 경우 모든 파라미터를 가져오지 못한다는 단점이 있다.
public async getParametersByPath( path: string, decrypt = false, ): Promise<Parameter[]> { const getParameters = new GetParametersByPathCommand({ Path: path, WithDecryption: decrypt, }); const { Parameters = [] } = await this.client.send(getParameters); return Parameters; }
TypeScript
복사

나의 nestjs-parameter-store

적잖은 관심을 받아서 기쁘다.
이에 기존의 라이브러리들의 단점들을 보완하고 더 많은 편의성을 제공하는 라이브러리를 개발했다.
static forRoot(options: SSMClientConfig): DynamicModule { const configProvider = configRegisterProvider(options); return { module: NestjsParameterStoreModule, providers: [configProvider, ssmClientProvider, AwsParamStoreService], exports: [AwsParamStoreService, SSM_CLIENT], }; }
TypeScript
복사
모듈에 options 타입을 SSMClientConfig로 지정함으로써 accessKey를 설정할 수 있게 하였다.
또한 NextToken에 대한 처리를 진행했다.
async getParametersByPath( options: GetParamByPathRequest, ): Promise<Parameter[] | Record<string, unknown>> { const parameters: Parameter[] = []; let parametersObject = {}; let nextToken: string | undefined = undefined; const { OnlyValue, ...option } = options; do { const result: { Parameters?: Parameter[]; NextToken?: string } = await this.ssmClient.send( new GetParametersByPathCommand({ ...option, NextToken: nextToken }), ); if (result?.Parameters) { if (OnlyValue) { parametersObject = { ...parametersObject, ...this.getValueObject(result.Parameters) }; } else { parameters.push(...(result?.Parameters ?? [])); } } nextToken = result?.NextToken; } while (nextToken); return OnlyValue ? parametersObject : parameters; }
TypeScript
복사
추가적으로 OnlyValue 옵션을 추가함으로써 파라미터의 Name과 Value만 가져올 수 있는 기능을 추가했다.
기존에는 파라미터들을 가져오면 아래와 같이 불필요할 수 있는 정보까지 전부 가져온다.
[ { "Name": "t", "Type": "String", "Value": "", "Version": 1, "LastModifiedDate": "", "ARN": "", "DataType": "text" }, { "Name": "", "Type": "String", "Value": "", "Version": 1, "LastModifiedDate": "", "ARN": "", "DataType": "text" } ]
TypeScript
복사
이에 사용자가 파라미터의 값을 쓰기 위해선 한번 더 처리를 해줘야 했다.
이를 OnlyValue 옵션으로 편의성을 제공했다.
// only-value.interface.ts export interface GetParamRequest extends GetParameterRequest { OnlyValue?: boolean; } export interface GetParamsRequest extends GetParametersRequest { OnlyValue?: boolean; } export interface GetParamByPathRequest extends GetParametersByPathRequest { OnlyValue?: boolean; } // aws-param-store.service.ts private getValueObject(params: Parameter[]) { return params.reduce((acc: any, param: Parameter) => { const name = param?.Name?.split("/").pop(); if (!name) { return acc; } if (!Number.isNaN(Number(param.Value))) { acc[name] = Number(param.Value); return acc; } acc[name] = param.Value; return acc; }, {}); }
TypeScript
복사
OnlyValue 옵션을 사용할 경우 아래와 같이 파라미터들이 제공된다.
{ Name: Value, Name: Value, ... }
TypeScript
복사