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

Support AWS KMS for signing and verifying pipelines #2960

Merged
merged 12 commits into from
Sep 2, 2024

Conversation

wolfeidau
Copy link
Contributor

@wolfeidau wolfeidau commented Aug 29, 2024

Description

This adds AWS KMS support to pipeline signing, it is a work in progress and I am pushing this up for some feedback.

Context

To outline how this will look I have put together a list of commands with the new AWS KMS option added.

The updated agent start command with a KMS key alias which is used to sign and verify pipelines.

buildkite-agent start --signing-aws-kms-key alias/ES256test --debug --debug-signing

To upload and sign a pipeline with a specific AWS KMS key specified. Note this can be omitted if your key is configured in the agent.

buildkite-agent pipeline upload --signing-aws-kms-key alias/ES256test --debug-signing --debug .buildkite/pipeline.yaml

The updated sign and upload command stored in buildkite with a KMS key alias.

buildkite-agent tool sign --aws-kms-key alias/ES256test \
    --graphql-token bkua_XXXXX --organization-slug bk-whatever --pipeline-slug build-golang-sample \
    --update --debug --debug-signing

This table provides a mapping for the supported KMS KeySpec to JSON Web Algorithms (JWA).

"alg" Param Value Digital Signature Algorithm KMS KeySpec
ES256 ECDSA using P-256 and SHA-256 ECC_NIST_P256
ES384 ECDSA using P-384 and SHA-384 ECC_NIST_P384
ES512 ECDSA using P-521 and SHA-512 ECC_NIST_P521
RS256 RSASSA-PKCS1-v1_5 using SHA-256 RSASSA_PKCS1_V1_5_SHA_256
RS384 RSASSA-PKCS1-v1_5 using SHA-384 RSASSA_PKCS1_V1_5_SHA_384
RS512 RSASSA-PKCS1-v1_5 using SHA-512 RSASSA_PKCS1_V1_5_SHA_512

So to create a KMS key which supports JSON Web Algorithms (JWA) ES256 we run the following command. Note the --key-usage SIGN_VERIFY is required to support signing of pipelines.

aws kms create-key --key-spec ECC_NIST_P256 --key-usage SIGN_VERIFY

Changes

Testing

  • Tests have run locally (with go test ./...). Buildkite employees may check this if the pipeline has run automatically.
  • Code is formatted (with go fmt ./...)

@wolfeidau wolfeidau changed the title feat kms signing and verifying Support AWS KMS for signing and verifying pipelines Aug 29, 2024
Copy link
Contributor

@patrobinson patrobinson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty good, some small improvements to naming of the flags would be good

clicommand/agent_start.go Outdated Show resolved Hide resolved
@@ -78,6 +81,7 @@ type PipelineUploadConfig struct {
// Used for signing
JWKSFile string `cli:"jwks-file"`
JWKSKeyID string `cli:"jwks-key-id"`
JWKSKMSKeyID string `cli:"jwks-kms-key-id"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
JWKSKMSKeyID string `cli:"jwks-kms-key-id"`
SigningAWSKMSKey string `cli:"signing-aws-kms-key"`

Spit balling here


switch {
case cfg.JWKSKMSKeyID != "":
awscfg, err := config.LoadDefaultConfig(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a small risk that this could resolve credentials in a different order to the how the rest of the agent does it.
But since this is a new feature, and we need to move to this way soon (when v1 of the SDK gets deprecated), I think this is a good place to start using this approach


output, err := sv.client.GetPublicKey(context.Background(), &input)
if err != nil {
return nil, fmt.Errorf(`failed to get public key from KMS: %w`, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this error just gets ignored should we log it?

// needs is setup with the algorithm name to use (see
// https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/kms/types#SigningAlgorithmSpec),
// a key ID to use while the AWS SDK makes network
// requests.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit hard to read, but you suggested it's now out of date anyway?

@wolfeidau wolfeidau force-pushed the feat_kms_signing_and_verifying branch from ea8b60e to 31c09ee Compare August 30, 2024 04:59
@wolfeidau wolfeidau force-pushed the feat_kms_signing_and_verifying branch from d57cc5c to c97f8a6 Compare September 2, 2024 01:24
@wolfeidau wolfeidau marked this pull request as ready for review September 2, 2024 02:32
@wolfeidau wolfeidau requested a review from DrJosh9000 September 2, 2024 02:32
Copy link
Contributor

@DrJosh9000 DrJosh9000 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some nits for you

// this is currently loaded here to ensure it is ONLY loaded if the agent is using KMS for signing
// this will limit the possible impact of this new SDK on the rest of the agent users
awscfg, err := config.LoadDefaultConfig(
context.Background(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a ctx in this function, may as well use it

Suggested change
context.Background(),
ctx,

Comment on lines 921 to 927
case cfg.VerificationJWKSFile != "":
if cfg.VerificationJWKSFile != "" {
var err error
verificationJWKS, err = parseAndValidateJWKS(ctx, "verification", cfg.VerificationJWKSFile)
if err != nil {
l.Fatal("Verification JWKS failed validation: %v", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant check is redundant:

Suggested change
case cfg.VerificationJWKSFile != "":
if cfg.VerificationJWKSFile != "" {
var err error
verificationJWKS, err = parseAndValidateJWKS(ctx, "verification", cfg.VerificationJWKSFile)
if err != nil {
l.Fatal("Verification JWKS failed validation: %v", err)
}
case cfg.VerificationJWKSFile != "":
var err error
verificationJWKS, err = parseAndValidateJWKS(ctx, "verification", cfg.VerificationJWKSFile)
if err != nil {
l.Fatal("Verification JWKS failed validation: %v", err)

Comment on lines 293 to 295
awscfg, err := config.LoadDefaultConfig(
context.Background(),
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
awscfg, err := config.LoadDefaultConfig(
context.Background(),
)
awscfg, err := config.LoadDefaultConfig(ctx)

Comment on lines 193 to 195
awscfg, err := config.LoadDefaultConfig(
context.Background(),
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
awscfg, err := config.LoadDefaultConfig(
context.Background(),
)
awscfg, err := config.LoadDefaultConfig(ctx)

ErrInvalidKeyID = fmt.Errorf(`invalid key ID`)
)

type KMS struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
type KMS struct {
// KMS is a crypto.Signer that uses an AWS KMS key for signing.
type KMS struct {

... or similar.


keyDesc, err := client.GetPublicKey(context.Background(), &kms.GetPublicKeyInput{KeyId: aws.String(kmsKeyID)})
if err != nil {
return nil, fmt.Errorf(`failed to describe key %q: %w`, kmsKeyID, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We began preferring "-strings where possible instead of backtick-strings for most uses a while ago. If nothing else, this file is inconsistent with the rest of the PR.

Suggested change
return nil, fmt.Errorf(`failed to describe key %q: %w`, kmsKeyID, err)
return nil, fmt.Errorf("failed to describe key %q: %w", kmsKeyID, err)

return pubkey
}

// This method is an escape hatch for those cases where the user needs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// This method is an escape hatch for those cases where the user needs
// GetPublicKey is an escape hatch for those cases where the user needs

return key, nil
}

// jwa.ES256
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of date?

Suggested change
// jwa.ES256
// Algorithm returns the equivalent of the KMS key's signing algorithm as a JWA key algorithm.

Copy link
Contributor

@DrJosh9000 DrJosh9000 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice stuff 👍

@wolfeidau wolfeidau force-pushed the feat_kms_signing_and_verifying branch from 640a107 to 059b464 Compare September 2, 2024 05:13
@wolfeidau
Copy link
Contributor Author

Some nits for you

Amazing, I have been through and addressed all your feedback 🙇🏻

@wolfeidau wolfeidau merged commit b10f0c3 into main Sep 2, 2024
1 check passed
@wolfeidau wolfeidau deleted the feat_kms_signing_and_verifying branch September 2, 2024 06:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants