Skip to content
This repository has been archived by the owner on Aug 7, 2023. It is now read-only.

Commit

Permalink
Generate ECDSA keypairs (#19)
Browse files Browse the repository at this point in the history
Generate ECDSA keypairsThese are smaller so we can inject them into the lambda environment easily
  • Loading branch information
Eduardo Lopez authored and czimergebot committed Jan 25, 2019
1 parent bef3d4c commit cde350b
Show file tree
Hide file tree
Showing 680 changed files with 67,698 additions and 14,912 deletions.
293 changes: 182 additions & 111 deletions Gopkg.lock

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ data "bless_lambda" "code" {
resource "aws_lambda_function" "bless" {
filename = "${path.module}/bless.zip"
source_code_hash = "${data.bless_lambda.code.output_base64sha256}"
...
}
```
3 changes: 2 additions & 1 deletion pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ func Provider() *schema.Provider {
},
},
ResourcesMap: map[string]*schema.Resource{
"bless_ca": CA(),
"bless_ca": CA(),
"bless_ecdsa_ca": ECDSACA(),
},
DataSourcesMap: map[string]*schema.Resource{
"bless_lambda": Lambda(),
Expand Down
1 change: 0 additions & 1 deletion pkg/provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"testing"

"github.com/aws/aws-sdk-go/service/kms/kmsiface"

"github.com/aws/aws-sdk-go/service/kms"
"github.com/chanzuckerberg/terraform-provider-bless/pkg/aws"
"github.com/chanzuckerberg/terraform-provider-bless/pkg/provider"
Expand Down
55 changes: 6 additions & 49 deletions pkg/provider/resource_ca.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
package provider

import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"

"github.com/chanzuckerberg/terraform-provider-bless/pkg/aws"
"github.com/chanzuckerberg/terraform-provider-bless/pkg/util"
"github.com/hashicorp/terraform/helper/schema"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh"
)

const (
Expand Down Expand Up @@ -68,12 +63,6 @@ func newResourceCA() *resourceCA {
return &resourceCA{}
}

type keyPair struct {
publicKey string
b64EncryptedPrivateKey string
password []byte
}

// Create creates a CA
func (ca *resourceCA) Create(d *schema.ResourceData, meta interface{}) error {
awsClient, ok := meta.(*aws.Client)
Expand All @@ -86,15 +75,15 @@ func (ca *resourceCA) Create(d *schema.ResourceData, meta interface{}) error {
if err != nil {
return err
}
encryptedPassword, err := awsClient.KMS.EncryptBytes(keyPair.password, kmsKeyID)
encryptedPassword, err := awsClient.KMS.EncryptBytes(keyPair.Password, kmsKeyID)
if err != nil {
return err
}

d.Set(schemaEncryptedPrivateKey, keyPair.b64EncryptedPrivateKey)
d.Set(schemaPublicKey, keyPair.publicKey)
d.Set(schemaEncryptedPrivateKey, keyPair.B64EncryptedPrivateKey)
d.Set(schemaPublicKey, keyPair.PublicKey)
d.Set(schemaEncryptedPassword, encryptedPassword)
d.SetId(util.HashForState(keyPair.publicKey))
d.SetId(util.HashForState(keyPair.PublicKey))
return nil
}

Expand All @@ -110,43 +99,11 @@ func (ca *resourceCA) Delete(d *schema.ResourceData, meta interface{}) error {
}

// ------------ helpers ------------------
func (ca *resourceCA) createKeypair() (*keyPair, error) {
func (ca *resourceCA) createKeypair() (*util.CA, error) {
// generate private key
privateKey, err := rsa.GenerateKey(rand.Reader, keySize)
if err != nil {
return nil, errors.Wrap(err, "Private key generation failed")
}
block := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
}

// generate password
password, err := util.GenerateRandomBytes(caPasswordBytes)
if err != nil {
return nil, errors.Wrap(err, "Could not generate enough random bytes")
}

// encrypt the private key
block, err = x509.EncryptPEMBlock(rand.Reader, block.Type, block.Bytes, password, x509.PEMCipherAES256)
if err != nil {
return nil, errors.Wrap(err, "Failed to encrypt CA")
}
var encryptedPEMBytes bytes.Buffer
err = pem.Encode(&encryptedPEMBytes, block)
if err != nil {
return nil, errors.Wrap(err, "Could not PEM encode encrypted CA")
}

// public key in openssh format
sshPublicKey, err := ssh.NewPublicKey(&privateKey.PublicKey)
if err != nil {
return nil, errors.Wrap(err, "Could not generate openssh public key")
}

return &keyPair{
publicKey: string(ssh.MarshalAuthorizedKey(sshPublicKey)),
b64EncryptedPrivateKey: base64.StdEncoding.EncodeToString(encryptedPEMBytes.Bytes()),
password: password,
}, nil
return util.NewCA(privateKey, privateKey.Public(), caPasswordBytes)
}
3 changes: 3 additions & 0 deletions pkg/provider/resource_ca_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ func TestCreate(t *testing.T) {
a.Regexp(
regexp.MustCompile("^-----BEGIN RSA PRIVATE KEY-----"),
string(bytesPrivate))
a.Regexp(
regexp.MustCompile(`AES-256-CBC`),
string(bytesPrivate))

publicSSHUntyped := s.RootModule().Outputs["public_key"].Value
publicSSH, ok := publicSSHUntyped.(string)
Expand Down
101 changes: 101 additions & 0 deletions pkg/provider/resource_ecdsa_ca.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package provider

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"

"github.com/chanzuckerberg/terraform-provider-bless/pkg/aws"
"github.com/chanzuckerberg/terraform-provider-bless/pkg/util"
"github.com/hashicorp/terraform/helper/schema"
"github.com/pkg/errors"
)

// ECDSACA is an ecdsa CA resource
func ECDSACA() *schema.Resource {
ca := newResourceECDSACA()
return &schema.Resource{
Create: ca.Create,
Read: ca.Read,
Delete: ca.Delete,

Schema: map[string]*schema.Schema{
schemaKmsKeyID: &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "The kms key with which we should encrypt the CA password.",
ForceNew: true,
},

// computed
schemaEncryptedPrivateKey: &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "This is the base64 encoded CA encrypted private key.",
},
schemaPublicKey: &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "This is the plaintext CA public key in openssh format.",
},
schemaEncryptedPassword: &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "This is the kms encrypted password.",
},
},
}
}

// resourceCA is a namespace
type resourceECDSACA struct{}

func newResourceECDSACA() *resourceECDSACA {
return &resourceECDSACA{}
}

// Create creates a CA
func (ca *resourceECDSACA) Create(d *schema.ResourceData, meta interface{}) error {
awsClient, ok := meta.(*aws.Client)
if !ok {
return errors.New("meta is not of type *aws.Client")
}

kmsKeyID := d.Get(schemaKmsKeyID).(string)
keyPair, err := ca.createKeypair()
if err != nil {
return err
}

encryptedPassword, err := awsClient.KMS.EncryptBytes(keyPair.Password, kmsKeyID)
if err != nil {
return err
}

d.Set(schemaEncryptedPrivateKey, keyPair.B64EncryptedPrivateKey)
d.Set(schemaPublicKey, keyPair.PublicKey)
d.Set(schemaEncryptedPassword, encryptedPassword)
d.SetId(util.HashForState(keyPair.PublicKey))
return nil
}

// Read reads the ca
func (ca *resourceECDSACA) Read(d *schema.ResourceData, meta interface{}) error {
return nil
}

// Delete deletes the ca
func (ca *resourceECDSACA) Delete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
return nil
}

// ------------ helpers ------------------
func (ca *resourceECDSACA) createKeypair() (*util.CA, error) {
// generate private key
privateKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
return nil, errors.Wrap(err, "Private key generation failed")
}
return util.NewCA(privateKey, privateKey.Public(), caPasswordBytes)
}
74 changes: 74 additions & 0 deletions pkg/provider/resource_ecdsa_ca_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package provider_test

import (
"crypto/rand"
"encoding/base64"
"regexp"
"testing"

"github.com/aws/aws-sdk-go/service/kms"
r "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

func TestCreateECDSA(t *testing.T) {
a := assert.New(t)
providers, kmsMock := getTestProviders()

ciphertext := make([]byte, 10)
rand.Read(ciphertext)
output := &kms.EncryptOutput{
CiphertextBlob: ciphertext,
}
kmsMock.On("Encrypt", mock.Anything).Return(output, nil)

r.Test(t, r.TestCase{
Providers: providers,
Steps: []r.TestStep{
r.TestStep{
Config: `
provider "bless" {
region = "us-east-1"
}
resource "bless_ecdsa_ca" "bless" {
kms_key_id = "testo"
}
output "ecdsa_private_key" {
value = "${bless_ecdsa_ca.bless.encrypted_ca}"
}
output "ecdsa_public_key" {
value = "${bless_ecdsa_ca.bless.public_key}"
}
output "ecdsa_password" {
value = "${bless_ecdsa_ca.bless.encrypted_password}"
}
`,
Check: func(s *terraform.State) error {
privateUntyped := s.RootModule().Outputs["ecdsa_private_key"].Value
private, ok := privateUntyped.(string)
a.True(ok)
bytesPrivate, err := base64.StdEncoding.DecodeString(private)
a.Nil(err)
a.Regexp(
regexp.MustCompile("^-----BEGIN EC PRIVATE KEY-----"),
string(bytesPrivate))
a.Regexp(
regexp.MustCompile(`AES-256-CBC`),
string(bytesPrivate))
publicSSHUntyped := s.RootModule().Outputs["ecdsa_public_key"].Value
publicSSH, ok := publicSSHUntyped.(string)
a.True(ok)
a.Regexp(
regexp.MustCompile("^ecdsa-sha2-nistp521 "),
string(publicSSH))
return nil
},
},
},
})
}

Loading

0 comments on commit cde350b

Please sign in to comment.