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

Add pubkey generation support #11

Merged
merged 11 commits into from
Mar 3, 2018
Merged

Conversation

FrenchBen
Copy link

Although this may not be very common, I often find myself having to feed a private key to Terraform, but then needing its public key for provider specific deployments.

This adds an equivalent public key RSA generator to the current private key, and moves the publicKey function to the public key file.

The input is a private key string, or file.

Checking acceptance tests:

$ make testacc
==> Checking that code complies with gofmt requirements...
TF_ACC=1 go test $(go list ./... |grep -v 'vendor') -v  -timeout 120m
?   	github.com/terraform-providers/terraform-provider-tls	[no test files]
=== RUN   TestProvider
--- PASS: TestProvider (0.00s)
=== RUN   TestCertRequest
--- PASS: TestCertRequest (0.04s)
=== RUN   TestLocallySignedCert
--- PASS: TestLocallySignedCert (0.02s)
=== RUN   TestPrivateKeyRSA
--- PASS: TestPrivateKeyRSA (3.21s)
=== RUN   TestPrivateKeyECDSA
--- PASS: TestPrivateKeyECDSA (0.05s)
=== RUN   TestPublicKeyRSA
--- PASS: TestPublicKeyRSA (4.69s)
=== RUN   TestSelfSignedCert
--- PASS: TestSelfSignedCert (0.04s)
PASS
ok  	github.com/terraform-providers/terraform-provider-tls/tls	8.077s

French Ben added 5 commits February 6, 2018 17:54
Signed-off-by: French Ben <frenchben@docker.com>
Signed-off-by: French Ben <frenchben@docker.com>
Signed-off-by: French Ben <frenchben@docker.com>
Signed-off-by: French Ben <frenchben@docker.com>
Signed-off-by: French Ben <frenchben@docker.com>
@FrenchBen
Copy link
Author

There's a good amount of duplicate code with the tls_private_key module, perhaps we can simply add support to the generation to have a private key string submitted, in which case it's not generated?

@vancluever
Copy link
Contributor

vancluever commented Feb 15, 2018

Hey @FrenchBen, this might also answer your second question as well, but I think this would be better as a data source. Main reason being is that a private key already needs to exist to extract the public key from, so nothing is actually being created by the resource.

More specifically answering your question about reuse - you could probably take the extraction code starting here and throw it into a helper that both tls_private_key (the resource) and tls_public_key (the data source) could use. The code may need a bit of re-arrangement so that the resulting helper is only exporting the public fields, leaving the private fields to the resource only.

French Ben added 2 commits February 16, 2018 17:36
Signed-off-by: French Ben <frenchben@docker.com>
Signed-off-by: French Ben <frenchben@docker.com>
@FrenchBen
Copy link
Author

FrenchBen commented Feb 17, 2018

Updated to use a data_source instead, as recommended:

$ make testacc
==> Checking that code complies with gofmt requirements...
TF_ACC=1 go test $(go list ./... |grep -v 'vendor') -v  -timeout 120m
?   	github.com/terraform-providers/terraform-provider-tls	[no test files]
=== RUN   TestAccPublicKey_dataSource
--- PASS: TestAccPublicKey_dataSource (0.02s)
=== RUN   TestProvider
--- PASS: TestProvider (0.00s)
=== RUN   TestCertRequest
--- PASS: TestCertRequest (0.04s)
=== RUN   TestLocallySignedCert
--- PASS: TestLocallySignedCert (0.03s)
=== RUN   TestPrivateKeyRSA
--- PASS: TestPrivateKeyRSA (4.10s)
=== RUN   TestPrivateKeyECDSA
--- PASS: TestPrivateKeyECDSA (0.05s)
=== RUN   TestSelfSignedCert
--- PASS: TestSelfSignedCert (0.04s)
PASS
ok  	github.com/terraform-providers/terraform-provider-tls/tls	4.300s

Copy link
Contributor

@vancluever vancluever left a comment

Choose a reason for hiding this comment

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

@FrenchBen great job so far! Just got some review comments that I'd like to see changed before we merge, and also the resource is lacking documentation. Can you make sure you add that as well?

You can see some examples on existing documentation for this provider in the website directory, and if you want to preview it you can clone the terraform-website repository and follow the instructions there to get set up.

Thanks!

return &schema.Resource{
Read: dataSourcePublicKeyRead,
Schema: map[string]*schema.Schema{
"private_key": &schema.Schema{
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be changed to private_key_pem to bring it in line with the other resources in this provider?

Type: schema.TypeString,
Optional: true,
Description: "File path of the PEM formatted string to use as the private key",
ForceNew: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

ForceNew is not needed in data sources. This generally controls deletion/creation workflows, both of which data sources lack.

Description: "PEM formatted string to use as the private key",
},

"private_key_path": &schema.Schema{
Copy link
Contributor

@vancluever vancluever Feb 17, 2018

Choose a reason for hiding this comment

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

I'm actually wondering if maybe this key should just be removed - this removes a bit of complexity from the resource, and if someone needs to load a file from a local path, they can just use the file interpolation function.

Copy link
Author

Choose a reason for hiding this comment

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

Great point! It'll make things a lot easier.


func dataSourcePublicKeyRead(d *schema.ResourceData, meta interface{}) error {
// Read private key
bytes := []byte(d.Get("private_key").(string))
Copy link
Contributor

Choose a reason for hiding this comment

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

You could also use GetOk or GetOkExists here, which are specifically the exact ways you check for a value defined in Terraform.

keyPemBlock, _ := pem.Decode(bytes)

if keyPemBlock == nil || (keyPemBlock.Type != "RSA PRIVATE KEY" && keyPemBlock.Type != "EC PRIVATE KEY") {
return fmt.Errorf("failed to decode PEM block containing RSA private key")
Copy link
Contributor

Choose a reason for hiding this comment

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

The error message seems to indicate that you are only attempting to decode an RSA private key even though the key can be that or an EC private key.

return fmt.Errorf("error converting key to rsa %s", err)
}

d.Set("private_key_pem", keyPem)
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like you have the field here named private_key_pem even though the field is just private_key in the schema. I'm recommending the schema be fixed, of course. :)

tls/util.go Outdated
@@ -74,3 +75,29 @@ func parseCertificateRequest(d *schema.ResourceData, pemKey string) (*x509.Certi

return certReq, nil
}

func parsePublicKey(d *schema.ResourceData, rsaKey interface{}) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor can we change this to readPublicKey to imply that it's doing a bit more than just parsing the public key?

}

const testAccDataSourcePublicKeyConfig = `
data "tls_public_key" "test" {
Copy link
Contributor

Choose a reason for hiding this comment

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

PS: You could probably make this test self-hosting but using the tls_private_key resource to generate a key. Then you could use TestCheckResourceAttrPair to just compare the two values.

Copy link
Author

@FrenchBen FrenchBen Feb 20, 2018

Choose a reason for hiding this comment

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

I'll add it as another test. I like the fact that we're taking a private key without it's matching Public Key and confirming that its output is what we expected.
The TestCheckResourceAttrPair seems redundant, as they both use the same function, thus if there's a mistake, it'll come be a consistent one.

Copy link
Contributor

Choose a reason for hiding this comment

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

@FrenchBen fair enough, and I actually didn't notice the testPrivateKey fixture being used in other tests. Will wait for the second test!

French Ben added 2 commits February 20, 2018 11:33
Signed-off-by: French Ben <frenchben@docker.com>
Signed-off-by: French Ben <frenchben@docker.com>
@FrenchBen
Copy link
Author

@vancluever PTAL at the latest changes :)

@vancluever
Copy link
Contributor

@FrenchBen sorry for leaving this so long - I'll have a look sometime within the next couple of days for sure. Were you going to work on adding the second test as mentioned?

@FrenchBen
Copy link
Author

FrenchBen commented Feb 28, 2018

@vancluever already did:
https://github.com/terraform-providers/terraform-provider-tls/pull/11/files#diff-db53aa662810ae9abc7cf890db96dc69R50

The 2 tests are:

  • Given a known private key, verify that the public key matches expected result.
  • Generate a private key, verify that the public key matches that of the generated public key (they use the same function, so it's a bit of an odd test).

Anything else you think I should test for?

@vancluever
Copy link
Contributor

Great - I didn't see that!

Barring anything serious, I think this can be merged tomorrow when I give it another once over 🙂

@FrenchBen
Copy link
Author

I think it's my fault - Looks like spacing is a little off, I'll get this fixed.

Signed-off-by: French Ben <frenchben@docker.com>
Copy link
Contributor

@vancluever vancluever left a comment

Choose a reason for hiding this comment

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

Hey @FrenchBen, one more comment about the tests, in addition to the following smoke test that failed:

This config:

resource "tls_private_key" "key" {
  algorithm   = "ECDSA"
  ecdsa_curve = "P384"
}

data "tls_public_key" "pub" {
  private_key_pem = "${tls_private_key.key.private_key_pem}"
}

output "data_public_key_pem" {
  value = "${data.tls_public_key.pub.public_key_pem}"
}

output "data_public_key_openssh" {
  value = "${data.tls_public_key.pub.public_key_openssh}"
}

Gives this error:

Error: Error applying plan:

1 error(s) occurred:

* data.tls_public_key.pub: data.tls_public_key.pub: error converting key to rsa asn1: structure error: tags don't match (2 vs {class:0 tag:4 length:48 isCompound:false}) {optional:false explicit:false application:false defaultValue:<nil> tag:<nil> stringType:0 timeType:0 set:false omitEmpty:false}  @5

This works with RSA, so not too sure what's happening here, but the P384 test is what is in our examples for tls_private_key, and the public keys dump in that resource just fine.

If you can fix that it would be great (might be good to add another test with that config specifically), also you will need to pull as I just fixed up some test naming and docs, and was about to approve this before the smoke test failed.

Thanks!

resource "tls_private_key" "test" {
algorithm = "RSA"
}
output "private_key_pem" {
Copy link
Contributor

@vancluever vancluever Mar 1, 2018

Choose a reason for hiding this comment

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

Are these outputs actually being tested anywhere? I don't see any checks for them...

Can this test also be broken out into another test? As it's pretty much a completely different test case (manually supplied key versus generated one).

Copy link
Author

@FrenchBen FrenchBen Mar 2, 2018

Choose a reason for hiding this comment

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

The output can be removed indeed.
Since the test was for all Public Key, I thought they belonged within the same function, but as separate steps. Is that not the right approach?

Signed-off-by: French Ben <frenchben@docker.com>
@FrenchBen
Copy link
Author

Added the extra test - Different algo = different parser (of course 🤦‍♂️ ) - Moved to using the default private key parser in the certs to make things cleaner.

@vancluever
Copy link
Contributor

@FrenchBen this looks good! I've just split out the tests so that it's more clear what exactly we are testing in each step.

Otherwise, this fixed everything - smoke test works now, so I think we are good to merge!

Thank you very much for you contribution and your patience through the process!

@vancluever vancluever merged commit 7f8e015 into hashicorp:master Mar 3, 2018
@FrenchBen FrenchBen deleted the add-pubkey branch March 3, 2018 02:30
Copy link

I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active contributions.
If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 30, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants