Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

@planetarium/account-aws-kms #2962

Merged
merged 4 commits into from
Mar 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/bin/constants.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ npm_packages=(
"@planetarium/cli"
"@planetarium/tx"
"@planetarium/account"
"@planetarium/account-aws-kms"
"@planetarium/account-web3-secret-storage"
)

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/benchmarks-merged.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: Libplanet benchmark (for merge commits)
on:
push:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/benchmarks-pr.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: Libplanet benchmark (for pull requests)
on:
pull_request: {}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
on:
push:
branches:
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/check-build.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
# This workflow checks if the build (compilation) succeeds without any errors.
# Although the build is done in CircleCI as well, to speed up the build time
# some checks are turned off in CircleCI. To conduct the complete checks
Expand All @@ -7,8 +8,8 @@
# - https://github.com/planetarium/libplanet/pull/977
# - https://github.com/planetarium/libplanet/issues/976
on:
push: []
pull_request: []
push: null
pull_request: null
name: check-build

jobs:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/delete-old-artifacts.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: 'Delete old artifacts'
on:
schedule:
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
on:
push:
branches-ignore:
Expand All @@ -6,7 +7,7 @@ on:
- "*"
schedule:
- cron: 59 14 * * *
pull_request: []
pull_request: null
name: main

jobs:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/push-docker-image.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
on: push
name: push docker image
jobs:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/rebase.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
on:
issue_comment:
types: [created]
Expand Down
21 changes: 20 additions & 1 deletion .github/workflows/yarn.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: yarn
on:
push:
branches: ["*"]
tags: ["*"]
pull_request: null

jobs:
build:
Expand All @@ -18,7 +20,24 @@ jobs:
cache: yarn
- run: yarn install --immutable # TODO: cache and apply --immutable-cache --check-cache
- run: yarn build
- run: yarn test
- id: account-aws-kms-changes
uses: tj-actions/changed-files@v35
with:
files: "@planetarium/account-aws-kms"
- name: Run yarn test w/o AWS cred for PR unrelated to account-aws-kms
if: |
github.event_name == 'pull_request' &&
steps.account-aws-kms-changes.outputs.any_changed != 'true'
run: yarn test
- name: Run yarn test w/ AWS cred for push and PR related to account-aws-kms
if: |
github.event_name != 'pull_request' ||
steps.account-aws-kms-changes.outputs.any_changed == 'true'
run: yarn test
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ secrets.AWS_REGION }}
- run: yarn pack-all

libplanet-tools-npm-install-test:
Expand Down
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"streetsidesoftware.code-spell-checker",
"GitHub.vscode-pull-request-github",
"arcanis.vscode-zipfs",
"rome.rome"
"rome.rome",
"redhat.vscode-yaml"
]
}
2 changes: 2 additions & 0 deletions @planetarium/account-aws-kms/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
coverage/
dist/
27 changes: 27 additions & 0 deletions @planetarium/account-aws-kms/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Contribution guide
==================

*See the monorepository's [contribution guide](../../CONTRIBUTING.md) as well.*


Automated tests
---------------

The automated tests are closed to functional tests rather than unit tests.
It requires AWS credentials to run tests, and each test run charges a fee.
Therefore, you should avoid to repeatedly run tests like a habit even when you
change much.

The test suite requires the following environment variables to be configured:

- `AWS_ACCESS_KEY_ID`
- `AWS_SECRET_ACCESS_KEY`
- `AWS_REGION`

The account requires several KMS permissions. See also
the [*README.md*](README.md) for details.

Note that it creates and immediately delete multiple keys to utilize them as
fixture data. They are scheduled to be deleted in seven days. Repeated tests
may make your AWS KMS a mess, so be careful. We recommend you to run the test
suite on a region you usually don't use.
49 changes: 49 additions & 0 deletions @planetarium/account-aws-kms/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
@planetarium/account-aws-kms
============================

An npm package for providing `AwsKmsKeyStore`, an implementation of `KeyStore`
from *@planetarium/account* that uses AWS KMS as the backend.

Required permissions
--------------------

| Method | Required permissions | Required for `AwsKmsKeyStoreOptions.scopingTags` |
|-----------------------------|-------------------------------------|--------------------------------------------------|
| `AwsKmsKeyStore.list()` | `kms:ListKeys` | `kms:ListResourceTags` |
| `AwsKmsKeyStore.get()` | `kms:ListKeys` | `kms:ListResourceTags` |
| `AwsKmsKeyStore.generate()` | `kms:CreateKey`, `kms:GetPublicKey` | `kms:TagResource` |
| `AwsKmsKeyStore.delete()` | `kms:ScheduleKeyDeletion` | |
| `AwsKmsAccount.sign()`[^1] | `kms:Sign` | |

Replace `[NUMERIC_ROOT_ACCOUNT_ID]` with your [12-digit root account ID][AWSId]:

~~~~ json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:GetPublicKey",
"kms:ScheduleKeyDeletion",
"kms:DescribeKey",
"kms:ListResourceTags",
"kms:Sign",
"kms:TagResource"
],
"Resource": "arn:aws:kms:*:[NUMERIC_ROOT_ACCOUNT_ID]:key/*"
},
{
"Effect": "Allow",
"Action": [
"kms:ListKeys",
"kms:CreateKey"
],
"Resource": "*"
}
]
moreal marked this conversation as resolved.
Show resolved Hide resolved
}
~~~~

[^1]: An `AwsKmsAccount` instance can be obtained from `AwsKmsKeyStore.get()`.
[AWSId]: https://docs.aws.amazon.com/signin/latest/userguide/FindingYourAWSId.html
38 changes: 38 additions & 0 deletions @planetarium/account-aws-kms/examples/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { RawPrivateKey } from "@planetarium/account";
import { AwsKmsKeyStore, KMSClient } from "@planetarium/account-aws-kms";

// Expects the below environment variables:
// - AWS_ACCESS_KEY_ID
// - AWS_SECRET_ACCESS_KEY
const store = new AwsKmsKeyStore(
new KMSClient({
region: "ap-northeast-3",
}),
);

if (process.argv[2] === "list") {
for await (const key of store.list()) {
console.log(key);
}
} else if (process.argv[2] === "get") {
const keyId = process.argv[3];
if (keyId == null) {
console.error("error: missing key id");
process.exit(1);
}
const key = await store.get(keyId);
console.log(key);
} else if (process.argv[2] === "generate") {
console.log(await store.generate());
} else if (process.argv[2] === "delete") {
const keyId = process.argv[3];
if (keyId == null) {
console.error("error: missing key id");
process.exit(1);
}
const key = await store.delete(keyId);
console.log(key);
} else {
console.error("usage: cli.ts list|get|generate|delete");
process.exit(1);
}
44 changes: 44 additions & 0 deletions @planetarium/account-aws-kms/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "@planetarium/account-aws-kms",
"private": true,
"description": "Libplanet account implementation using AWS KMS",
"type": "module",
"main": "./dist/index.js",
"exports": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist/**/*"
],
"scripts": {
"build": "yarn && nanobundle build",
"prepack": "yarn && yarn build",
"dev": "yarn test",
"test": "bash -c 'yarn && yarn run -T tsc -p tsconfig.json && vitest run; status=$?; printf \"\\033[41;97mNOTE: running this test suite too many times may be costly, as this creates and deletes AWS KMS keys during testing. KMS keys are prorated per hour for the price of \\$1.00 per key.\\033[0m\n\" > /dev/stderr; exit $status'",
"coverage": "yarn && vitest run --coverage"
},
"repository": {
"type": "git",
"url": "git+https://github.com/planetarium/libplanet.git",
"directory": "@planetarium/account-aws-kms"
},
"keywords": [
"libplanet"
],
"author": "Planetarium (https://planetarium.dev/)",
"license": "LGPL-2.1-or-later",
"devDependencies": {
"@types/node": "^18.13.0",
"@vitest/coverage-c8": "^0.29.3",
"@vitest/ui": "^0.29.3",
"es-aggregate-error": "^1.0.9",
"nanobundle": "^1.5.0",
"vite": "^4.1.1",
"vitest": "^0.29.3"
},
"dependencies": {
"@aws-sdk/client-kms": "^3.272.0",
"@noble/secp256k1": "^1.7.1",
"@planetarium/account": "workspace:^",
"asn1js": "^3.0.5"
}
}
36 changes: 36 additions & 0 deletions @planetarium/account-aws-kms/src/AwsKmsAccount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { AwsKmsKeyId } from "./AwsKmsKeyId.js";
import { KMSClient, SignCommand } from "@aws-sdk/client-kms";
import { Signature as NobleSignature } from "@noble/secp256k1";
import {
type Account,
type Message,
PublicKey,
Signature,
} from "@planetarium/account";

export class AwsKmsAccount implements Account {
readonly #client: KMSClient;

readonly keyId: AwsKmsKeyId;
readonly publicKey: PublicKey;

constructor(keyId: AwsKmsKeyId, publicKey: PublicKey, client: KMSClient) {
this.keyId = keyId;
this.publicKey = publicKey;
this.#client = client;
}

async sign(message: Message): Promise<Signature> {
const cmd = new SignCommand({
KeyId: this.keyId,
Message: message,
SigningAlgorithm: "ECDSA_SHA_256",
});
const response = await this.#client.send(cmd);
if (response.Signature == null) throw new Error("Failed to sign message");
const sig = NobleSignature.fromDER(response.Signature).normalizeS();
Akamig marked this conversation as resolved.
Show resolved Hide resolved
return Signature.fromHex(sig.toDERHex());
}
}

export default AwsKmsAccount;
3 changes: 3 additions & 0 deletions @planetarium/account-aws-kms/src/AwsKmsKeyId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type AwsKmsKeyId = string; // UUID

export default AwsKmsKeyId;
Loading