Skip to content

Commit

Permalink
Implement KMS key decrypter
Browse files Browse the repository at this point in the history
  • Loading branch information
DarcyRaynerDD committed Jun 17, 2019
1 parent c21a875 commit 4ad5b4a
Show file tree
Hide file tree
Showing 10 changed files with 10,879 additions and 13 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ module github.com/DataDog/datadog-lambda-go
go 1.12

require (
github.com/aws/aws-sdk-go v1.19.40 // indirect
github.com/aws/aws-sdk-go v1.20.2
github.com/aws/aws-xray-sdk-go v1.0.0-rc.9
github.com/cenkalti/backoff v2.1.1+incompatible
github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect
github.com/pkg/errors v0.8.1 // indirect
github.com/stretchr/testify v1.3.0
)
15 changes: 15 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
github.com/aws/aws-sdk-go v1.19.40 h1:omRrS4bCM/IbzU6UEb8Ojg1PvlElZzYZkOh8vWWgFMc=
github.com/aws/aws-sdk-go v1.19.40/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.20.2 h1:/BBeW8F4PPmvJ5jpFvgkCK4RJQXErNndVRnNhO2qEkQ=
github.com/aws/aws-sdk-go v1.20.2/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-xray-sdk-go v1.0.0-rc.9 h1:MC5zypTWx5YIbWE3pgcPaG8+1ytirvfCVBkcgHbVZ5Q=
github.com/aws/aws-xray-sdk-go v1.0.0-rc.9/go.mod h1:XtMKdBQfpVut+tJEwI7+dJFRxxRdxHDyVNp2tHXRq04=
github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs=
github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
50 changes: 43 additions & 7 deletions internal/metrics/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,19 @@ type (

// APIClient send metrics to Datadog, via the Datadog API
APIClient struct {
apiKey string
apiKey string
apiKeyDecryptChan <-chan string
baseAPIURL string
httpClient *http.Client
context context.Context
}

// APIClientOptions contains instantiation options from creating an APIClient.
APIClientOptions struct {
baseAPIURL string
httpClient *http.Client
context context.Context
apiKey string
kmsAPIKey string
decrypter Decrypter
}

postMetricsModel struct {
Expand All @@ -39,18 +48,30 @@ type (
)

// MakeAPIClient creates a new API client with the given api and app keys
func MakeAPIClient(ctx context.Context, baseAPIURL, apiKey string) *APIClient {
func MakeAPIClient(ctx context.Context, options APIClientOptions) *APIClient {
httpClient := &http.Client{}
return &APIClient{
apiKey: apiKey,
baseAPIURL: baseAPIURL,
client := &APIClient{
apiKey: options.apiKey,
baseAPIURL: options.baseAPIURL,
httpClient: httpClient,
context: ctx,
}
if len(options.apiKey) == 0 && len(options.kmsAPIKey) != 0 {
client.apiKeyDecryptChan = client.decryptAPIKey(options.decrypter, options.kmsAPIKey)
}

return client
}

// SendMetrics posts a batch metrics payload to the Datadog API
func (cl *APIClient) SendMetrics(metrics []APIMetric) error {

// If the api key was provided as a kms key, wait for it to finish decrypting
if cl.apiKeyDecryptChan != nil {
cl.apiKey = <-cl.apiKeyDecryptChan
cl.apiKeyDecryptChan = nil
}

content, err := marshalAPIMetricsModel(metrics)
if err != nil {
return fmt.Errorf("Couldn't marshal metrics model: %v", err)
Expand Down Expand Up @@ -92,6 +113,21 @@ func (cl *APIClient) SendMetrics(metrics []APIMetric) error {
return nil
}

func (cl *APIClient) decryptAPIKey(decrypter Decrypter, kmsAPIKey string) <-chan string {

ch := make(chan string)

go func() {
result, err := decrypter.Decrypt(kmsAPIKey)
if err != nil {
logger.Error(fmt.Errorf("Couldn't decrypt api kms key %s", err))
}
ch <- result
close(ch)
}()
return ch
}

func (cl *APIClient) addAPICredentials(req *http.Request) {
query := req.URL.Query()
query.Add(apiKeyParam, cl.apiKey)
Expand Down
8 changes: 4 additions & 4 deletions internal/metrics/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const (
)

func TestAddAPICredentials(t *testing.T) {
cl := MakeAPIClient(context.Background(), "", mockAPIKey)
cl := MakeAPIClient(context.Background(), APIClientOptions{baseAPIURL: "", apiKey: mockAPIKey})
req, _ := http.NewRequest("GET", "http://some-api.com/endpoint", nil)
cl.addAPICredentials(req)
assert.Equal(t, "http://some-api.com/endpoint?api_key=12345", req.URL.String())
Expand Down Expand Up @@ -57,7 +57,7 @@ func TestSendMetricsSuccess(t *testing.T) {
},
}

cl := MakeAPIClient(context.Background(), server.URL, mockAPIKey)
cl := MakeAPIClient(context.Background(), APIClientOptions{baseAPIURL: server.URL, apiKey: mockAPIKey})
err := cl.SendMetrics(am)

assert.NoError(t, err)
Expand Down Expand Up @@ -92,7 +92,7 @@ func TestSendMetricsBadRequest(t *testing.T) {
},
}

cl := MakeAPIClient(context.Background(), server.URL, mockAPIKey)
cl := MakeAPIClient(context.Background(), APIClientOptions{baseAPIURL: server.URL, apiKey: mockAPIKey})
err := cl.SendMetrics(am)

assert.Error(t, err)
Expand Down Expand Up @@ -120,7 +120,7 @@ func TestSendMetricsCantReachServer(t *testing.T) {
},
}

cl := MakeAPIClient(context.Background(), "httpa:///badly-formatted-url", mockAPIKey)
cl := MakeAPIClient(context.Background(), APIClientOptions{baseAPIURL: "httpa:///badly-formatted-url", apiKey: mockAPIKey})
err := cl.SendMetrics(am)

assert.Error(t, err)
Expand Down
55 changes: 55 additions & 0 deletions internal/metrics/kms_decrypter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed
* under the Apache License Version 2.0.
*
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2019 Datadog, Inc.
*/
package metrics

import (
"encoding/base64"
"fmt"

"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/kms"
)

type (
// Decrypter attempts to decrypt a key
Decrypter interface {
Decrypt(cipherText string) (string, error)
}

kmsDecrypter struct {
kmsClient *kms.KMS
}
)

// MakeKMSDecrypter creates a new decrypter which uses the AWS KMS service to decrypt variables
func MakeKMSDecrypter() Decrypter {
return &kmsDecrypter{
kmsClient: kms.New(session.New(nil)),
}
}

func (kd *kmsDecrypter) Decrypt(cipherText string) (string, error) {

decodedBytes, err := base64.StdEncoding.DecodeString(cipherText)
if err != nil {
return "", fmt.Errorf("Failed to encode cipher text to base64: %v", err)
}

params := &kms.DecryptInput{
CiphertextBlob: decodedBytes,
}

response, err := kd.kmsClient.Decrypt(params)
if err != nil {
return "", fmt.Errorf("Failed to decrypt ciphertext with kms: %v", err)
}
// Plaintext is a byte array, so convert to string
decrypted := string(response.Plaintext[:])

return decrypted, nil
}
2 changes: 1 addition & 1 deletion internal/metrics/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func MakeListener(config Config) Listener {
}
baseAPIURL := fmt.Sprintf("https://api.%s/api/v1", site)

apiClient := MakeAPIClient(context.Background(), baseAPIURL, config.APIKey)
apiClient := MakeAPIClient(context.Background(), APIClientOptions{baseAPIURL: baseAPIURL, apiKey: config.APIKey})
if config.BatchInterval <= 0 {
config.BatchInterval = defaultBatchInterval
}
Expand Down
Loading

0 comments on commit 4ad5b4a

Please sign in to comment.