From d66ef951202fd0cf895f4d3cd052c99c036e5335 Mon Sep 17 00:00:00 2001 From: Jonny Stoten Date: Fri, 3 Nov 2023 17:34:06 +0000 Subject: [PATCH 1/9] Add support for AWS KMS in tuf-on-ci-delegate --- repo/pyproject.toml | 2 +- signer/pyproject.toml | 2 +- signer/tuf_on_ci_sign/delegate.py | 11 +++++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/repo/pyproject.toml b/repo/pyproject.toml index 9c45076e..23b96d9f 100644 --- a/repo/pyproject.toml +++ b/repo/pyproject.toml @@ -12,7 +12,7 @@ version = "0.2.0" description = "TUF-on-CI repository tools, intended to be executed on a CI system" readme = "README.md" dependencies = [ - "securesystemslib[azurekms, gcpkms, sigstore, pynacl] ~= 0.30", + "securesystemslib[awskms, azurekms, gcpkms, sigstore, pynacl] ~= 0.30", "tuf ~= 3.0", "click ~= 8.1", ] diff --git a/signer/pyproject.toml b/signer/pyproject.toml index 5f55cd71..b3141715 100644 --- a/signer/pyproject.toml +++ b/signer/pyproject.toml @@ -12,7 +12,7 @@ version = "0.2.0" description = "Signing tools for TUF-on-CI" readme = "README.md" dependencies = [ - "securesystemslib[gcpkms,hsm,sigstore] ~= 0.30", + "securesystemslib[awskms,azurekms,gcpkms,hsm,sigstore] ~= 0.30", "tuf ~= 3.0", "click ~= 8.1", ] diff --git a/signer/tuf_on_ci_sign/delegate.py b/signer/tuf_on_ci_sign/delegate.py index 4da810a0..8fac16cf 100755 --- a/signer/tuf_on_ci_sign/delegate.py +++ b/signer/tuf_on_ci_sign/delegate.py @@ -12,6 +12,7 @@ import click from securesystemslib.signer import ( KEY_FOR_TYPE_AND_SCHEME, + AWSSigner, AzureSigner, GCPSigner, Key, @@ -196,6 +197,7 @@ def _collect_online_key(user_config: User) -> Key: click.echo(" 1. Sigstore") click.echo(" 2. Google Cloud KMS") click.echo(" 3. Azure Key Vault") + click.echo(" 4. AWS KMS") choice = click.prompt( bold("Please select online key type"), type=click.IntRange(1, 4), @@ -222,6 +224,15 @@ def _collect_online_key(user_config: User) -> Key: except Exception as e: raise click.ClickException(f"Failed to read Azure Keyvault key: {e}") if choice == 4: + key_id = _collect_string("Enter AWS KMS key id") + scheme = _collect_string("Enter key scheme") + try: + uri, key = AWSSigner.import_(key_id, scheme) + key.unrecognized_fields["x-tuf-on-ci-online-uri"] = uri + return key + except Exception as e: + raise click.ClickException(f"Failed to read AWS KMS key: {e}") + if choice == 5: # This could be generic support, but for now it's a hidden test key. # key value 1d9a024348e413892aeeb8cc8449309c152f48177200ee61a02ae56f450c6480 uri = "envvar:LOCAL_TESTING_KEY" From 7d79e58f031e891d04f0d5f7aa4f05200d83e04e Mon Sep 17 00:00:00 2001 From: Jonny Stoten Date: Fri, 3 Nov 2023 20:15:03 +0000 Subject: [PATCH 2/9] Authenticate to AWS --- actions/online-sign/action.yml | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/actions/online-sign/action.yml b/actions/online-sign/action.yml index 57d0e790..abaff93d 100644 --- a/actions/online-sign/action.yml +++ b/actions/online-sign/action.yml @@ -1,15 +1,23 @@ -name: 'Online sign' -description: 'Creates a snapshot and timestamp if needed, moves publish branch if needed' +name: "Online sign" +description: "Creates a snapshot and timestamp if needed, moves publish branch if needed" inputs: gcp_workload_identity_provider: - description: 'Google Cloud workload identity provider' + description: "Google Cloud workload identity provider" required: false - default: '' + default: "" gcp_service_account: - description: 'Google Cloud service account name' + description: "Google Cloud service account name" required: false - default: '' + default: "" + aws_region: + description: "AWS region" + required: false + default: "" + aws_role_to_assume: + description: "AWS role to assume" + required: false + default: "" runs: using: "composite" @@ -26,6 +34,13 @@ runs: workload_identity_provider: ${{ inputs.gcp_workload_identity_provider }} service_account: ${{ inputs.gcp_service_account }} + - name: Authenticate to AWS + if: inputs.aws_role_to_assume != '' + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: ${{ inputs.aws_region }} + role-to-assume: ${{ inputs.aws_role_to_assume }} + - uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1 with: python-version: "3.11" From a0d99c0142ac8703bb58eefe79683b3811efb261 Mon Sep 17 00:00:00 2001 From: Jonny Stoten Date: Wed, 8 Nov 2023 11:39:23 +0000 Subject: [PATCH 3/9] Choose scheme by multiple choice --- signer/tuf_on_ci_sign/delegate.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/signer/tuf_on_ci_sign/delegate.py b/signer/tuf_on_ci_sign/delegate.py index 8fac16cf..521e49cb 100755 --- a/signer/tuf_on_ci_sign/delegate.py +++ b/signer/tuf_on_ci_sign/delegate.py @@ -225,7 +225,7 @@ def _collect_online_key(user_config: User) -> Key: raise click.ClickException(f"Failed to read Azure Keyvault key: {e}") if choice == 4: key_id = _collect_string("Enter AWS KMS key id") - scheme = _collect_string("Enter key scheme") + scheme = _collect_key_scheme() try: uri, key = AWSSigner.import_(key_id, scheme) key.unrecognized_fields["x-tuf-on-ci-online-uri"] = uri @@ -255,6 +255,30 @@ def _collect_string(prompt: str) -> str: else: return data +def _collect_key_scheme() -> str: + scheme_choices = { + 1: {"ssllib": "ecdsa-sha2-nistp256", "aws": "ECDSA_SHA_256"}, + 2: {"ssllib": "ecdsa-sha2-nistp384", "aws": "ECDSA_SHA_384"}, + 3: {"ssllib": "ecdsa-sha2-nistp512", "aws": "ECDSA_SHA_512"}, + 4: {"ssllib": "rsassa-pss-sha256", "aws": "RSASSA_PSS_SHA_256"}, + 5: {"ssllib": "rsassa-pss-sha384", "aws": "RSASSA_PSS_SHA_384"}, + 6: {"ssllib": "rsassa-pss-sha512", "aws": "RSASSA_PSS_SHA_512"}, + 7: {"ssllib": "rsa-pkcs1v15-sha256", "aws": "RSASSA_PKCS1_V1_5_SHA_256"}, + 8: {"ssllib": "rsa-pkcs1v15-sha384", "aws": "RSASSA_PKCS1_V1_5_SHA_384"}, + 9: {"ssllib": "rsa-pkcs1v15-sha512", "aws": "RSASSA_PKCS1_V1_5_SHA_512"}, + } + + for key, value in scheme_choices.items(): + click.echo(f"{key}. {value['aws']}") + while True: + choice = click.prompt( + bold("Please select AWS key scheme"), + type=click.IntRange(1, 9), + default=1, + show_default=True, + ) + return scheme_choices[choice]["ssllib"] + def _init_repository(repo: SignerRepository) -> bool: click.echo("Creating a new TUF-on-CI repository") From eb19351b7fe78d70e9125f2a5754f0d0283a63b2 Mon Sep 17 00:00:00 2001 From: Jonny Stoten Date: Wed, 8 Nov 2023 11:43:23 +0000 Subject: [PATCH 4/9] Add docs for AWS KMS online keys --- README.md | 2 +- docs/ONLINE-SIGNING-SETUP.md | 14 ++++++++++++++ docs/REPOSITORY-MAINTENANCE.md | 6 +++--- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 651a6223..d764f5ce 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Supported features include: * Guided signing events for distributed signing * TUF delegations with signature thresholds * Signing with hardware keys and Sigstore -* Automated online signing (Google Cloud, Azure, Sigstore) +* Automated online signing (Google Cloud, Azure, AWS, Sigstore) * No custom code required The optimal use case is TUF repositories with a low to moderate frequency of change, both for artifacts and keys. diff --git a/docs/ONLINE-SIGNING-SETUP.md b/docs/ONLINE-SIGNING-SETUP.md index 5d7c1a8c..6a58b6bb 100644 --- a/docs/ONLINE-SIGNING-SETUP.md +++ b/docs/ONLINE-SIGNING-SETUP.md @@ -8,6 +8,7 @@ Currently supported signing methods include * Sigstore (experimental) * Google Cloud KMS * Azure Key Vault +* AWS KMS ## Configuration @@ -62,3 +63,16 @@ currently experimental (and not supported by all TUF client libraries) login](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) and authenticate against the environment where the key vault exists. You will need to the role _"Key Vault Crypto User"_). + +### AWS KMS + +1. Make sure AWS IAM permissions allow your GitHub repositorys OIDC identity to sign with a KMS key. +1. Define your authentication details as repository variables in _Settings->Secrets and variables->Actions->Variables_: + ``` + AWS_ROLE_TO_ASSUME: arn:aws:iam::175142243308:role/tuf-testing-online-key + AWS_REGION: us-east-1 + ``` +1. _(only needed for initial configuration)_ Prepare your local environment for accessing the cloud KMS: + Use the [AWS CLI](https://aws.amazon.com/cli/) and authenticate in the + environment where you plan to run `tuf-on-ci-delegate` tool (you will need permission to use + the KMS key). diff --git a/docs/REPOSITORY-MAINTENANCE.md b/docs/REPOSITORY-MAINTENANCE.md index 52995192..5d40080f 100644 --- a/docs/REPOSITORY-MAINTENANCE.md +++ b/docs/REPOSITORY-MAINTENANCE.md @@ -12,11 +12,11 @@ ongoing maintenance. `publish` 1. Clone the repository locally and [configure your local signing tool](SIGNER-SETUP.md) 1. Choose your online signing method and [configure it](ONLINE-SIGNING-SETUP.md): - * Google Cloud KMS and Azure Key Vault are fully supported + * Google Cloud KMS, Azure Key Vault, and AWS KMS are fully supported * Sigstore requires no configuration (but is experimental) 1. Run `tuf-on-ci-delegate sign/init` to configure the repository and to start the first signing event - * The tool prompts for various repository details and finally prompts to + * The tool prompts for various repository details and finally prompts to sign and push the initial metadata to a signing event branch 1. When this initial signing event branch is merged, the repository generates the first snapshot and timestamp, and publishes the first repository version @@ -31,7 +31,7 @@ Modifying a role is needed when: Roles are modified with `tuf-on-ci-delegate `. * The event name can be chosen freely (and will be used as a branch name). If the signing - event does not exist yet, it will be created as a result. + event does not exist yet, it will be created as a result. * The tool will prompt for new signers and other details, and then prompt to push changes to the repository. * The push triggers creation of a signing event GitHub issue. The repository will report the From ffaae11ff3c78a968cedc10ddc1793621455a3b6 Mon Sep 17 00:00:00 2001 From: Jonny Stoten Date: Thu, 9 Nov 2023 15:16:45 +0000 Subject: [PATCH 5/9] Remove loop --- signer/tuf_on_ci_sign/delegate.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/signer/tuf_on_ci_sign/delegate.py b/signer/tuf_on_ci_sign/delegate.py index 521e49cb..538f168f 100755 --- a/signer/tuf_on_ci_sign/delegate.py +++ b/signer/tuf_on_ci_sign/delegate.py @@ -270,14 +270,13 @@ def _collect_key_scheme() -> str: for key, value in scheme_choices.items(): click.echo(f"{key}. {value['aws']}") - while True: - choice = click.prompt( - bold("Please select AWS key scheme"), - type=click.IntRange(1, 9), - default=1, - show_default=True, - ) - return scheme_choices[choice]["ssllib"] + choice = click.prompt( + bold("Please select AWS key scheme"), + type=click.IntRange(1, 9), + default=1, + show_default=True, + ) + return scheme_choices[choice]["ssllib"] def _init_repository(repo: SignerRepository) -> bool: From a55b099be17df0fb1642b7b85a8611c31687200f Mon Sep 17 00:00:00 2001 From: Jonny Stoten Date: Tue, 14 Nov 2023 13:22:41 +0000 Subject: [PATCH 6/9] Fix formatting --- signer/tuf_on_ci_sign/delegate.py | 1 + 1 file changed, 1 insertion(+) diff --git a/signer/tuf_on_ci_sign/delegate.py b/signer/tuf_on_ci_sign/delegate.py index 538f168f..40e93d51 100755 --- a/signer/tuf_on_ci_sign/delegate.py +++ b/signer/tuf_on_ci_sign/delegate.py @@ -255,6 +255,7 @@ def _collect_string(prompt: str) -> str: else: return data + def _collect_key_scheme() -> str: scheme_choices = { 1: {"ssllib": "ecdsa-sha2-nistp256", "aws": "ECDSA_SHA_256"}, From 49aba01b04cc51139552d261a887e58b20a05f38 Mon Sep 17 00:00:00 2001 From: Jonny Stoten Date: Tue, 14 Nov 2023 13:59:01 +0000 Subject: [PATCH 7/9] Use 0 for local testing key and fix tests --- signer/tuf_on_ci_sign/delegate.py | 4 ++-- tests/e2e.sh | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/signer/tuf_on_ci_sign/delegate.py b/signer/tuf_on_ci_sign/delegate.py index 40e93d51..5820c908 100755 --- a/signer/tuf_on_ci_sign/delegate.py +++ b/signer/tuf_on_ci_sign/delegate.py @@ -200,7 +200,7 @@ def _collect_online_key(user_config: User) -> Key: click.echo(" 4. AWS KMS") choice = click.prompt( bold("Please select online key type"), - type=click.IntRange(1, 4), + type=click.IntRange(0, 4), default=1, show_default=True, ) @@ -232,7 +232,7 @@ def _collect_online_key(user_config: User) -> Key: return key except Exception as e: raise click.ClickException(f"Failed to read AWS KMS key: {e}") - if choice == 5: + if choice == 0: # This could be generic support, but for now it's a hidden test key. # key value 1d9a024348e413892aeeb8cc8449309c152f48177200ee61a02ae56f450c6480 uri = "envvar:LOCAL_TESTING_KEY" diff --git a/tests/e2e.sh b/tests/e2e.sh index 1e4977b7..54792a80 100755 --- a/tests/e2e.sh +++ b/tests/e2e.sh @@ -126,7 +126,7 @@ signer_init() "" # Configure root ? [enter to continue] "" # Configure targets? [enter to continue] "1" # Configure online roles? [1: configure key] - "4" # Enter online key type + "0" # Enter online key type "" # Configure online roles? [enter to continue] "2" # Choose signing key [2: yubikey] "" # Insert HW key and press enter @@ -213,7 +213,7 @@ signer_init_shorter_snapshot_expiry() "" # Configure root ? [enter to continue] "" # Configure targets? [enter to continue] "1" # Configure online roles? [1: configure key] - "4" # Enter online key type + "0" # Enter online key type "3" # Configure online roles? [3: configure snapshot] "10" # Enter expiry [10 days] "4" # Enter signing period [4 days] @@ -249,7 +249,7 @@ signer_init_multiuser() "2" # Enter threshold "" # Configure targets? [enter to continue] "1" # Configure online roles? [1: configure key] - "4" # Enter online key type + "0" # Enter online key type "" # Configure online roles? [enter to continue] "2" # Choose signing key [2: yubikey] "" # Insert HW key and press enter From 7eb01e10602d4b89c60a4f0ea09642fd2e95330b Mon Sep 17 00:00:00 2001 From: Jonny Stoten Date: Tue, 14 Nov 2023 14:28:11 +0000 Subject: [PATCH 8/9] Disable GPG signing in e2e tests and remove some duplication --- tests/e2e.sh | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/e2e.sh b/tests/e2e.sh index 54792a80..f93e5360 100755 --- a/tests/e2e.sh +++ b/tests/e2e.sh @@ -64,14 +64,21 @@ strip_signatures() rm "$1e2e" } +git_clone() +{ + REPO=$1 + REMOTE=$2 + USER_NAME=$3 + USER_EMAIL=$4 + git -C $REPO clone --quiet $REMOTE . 2>/dev/null + git -C $REPO config user.email "$USER_EMAIL" + git -C $REPO config user.name "$USER_NAME" + git -C $REPO config commit.gpgsign false +} + git_repo() { - git \ - -C $REPO_GIT \ - -c user.name=tuf-on-ci \ - -c user.email=41898282+github-actions[bot]@users.noreply.github.com \ - -c commit.gpgsign=false \ - $@ + git -C $REPO_GIT "$@" } repo_setup() @@ -80,7 +87,7 @@ repo_setup() git -C $UPSTREAM_GIT init --quiet --bare --initial-branch=main # Clone upstream to repo, create a dummy commit so merges are possible - git_repo clone --quiet $UPSTREAM_GIT . 2>/dev/null + git_clone $REPO_GIT $UPSTREAM_GIT "tuf-on-ci" "41898282+github-actions[bot]@users.noreply.github.com" touch $REPO_GIT/.dummy $REPO_DIR/out git_repo add .dummy git_repo commit -m "init" --quiet @@ -100,9 +107,7 @@ signer_setup() cp -r $SCRIPT_DIR/softhsm/tokens-$USER $SIGNER_DIR/tokens # clone the test repository - git -C $SIGNER_GIT clone --quiet $UPSTREAM_GIT . - git -C $SIGNER_GIT config user.email "$USER@example.com" - git -C $SIGNER_GIT config user.name "$USER" + git_clone $SIGNER_GIT $UPSTREAM_GIT "$USER@example.com" "$USER" # Set user configuration echo -e "[settings]\n" \ From d39e916dcdf82f22d76475d7ecb873c6235860b5 Mon Sep 17 00:00:00 2001 From: Jonny Stoten Date: Tue, 14 Nov 2023 14:30:47 +0000 Subject: [PATCH 9/9] Pin configure-aws-credentials action to commit --- actions/online-sign/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/online-sign/action.yml b/actions/online-sign/action.yml index abaff93d..4b091274 100644 --- a/actions/online-sign/action.yml +++ b/actions/online-sign/action.yml @@ -36,7 +36,7 @@ runs: - name: Authenticate to AWS if: inputs.aws_role_to_assume != '' - uses: aws-actions/configure-aws-credentials@v4 + uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a #v4.0.1 with: aws-region: ${{ inputs.aws_region }} role-to-assume: ${{ inputs.aws_role_to_assume }}