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 issuance of ACM private certificate #6666

Merged
merged 2 commits into from
Aug 6, 2019
Merged

Support issuance of ACM private certificate #6666

merged 2 commits into from
Aug 6, 2019

Conversation

micbase
Copy link
Contributor

@micbase micbase commented Nov 30, 2018

Fixes partial of #5550
For certificates issued by PCA, need a separate resource, and not covered in this PR

Changes proposed in this pull request:

  • Support issuance of ACM private certificate

Output from acceptance testing:

$ make testacc TESTARGS='-run=TestAccAWSAcmCertificate_privateCert'
==> Checking that code complies with gofmt requirements...
TF_ACC=1 go test ./... -v -parallel 20 -run=TestAccAWSAcmCertificate_privateCert -timeout 120m
?   	github.com/terraform-providers/terraform-provider-aws	[no test files]
=== RUN   TestAccAWSAcmCertificate_privateCert
=== PAUSE TestAccAWSAcmCertificate_privateCert
=== CONT  TestAccAWSAcmCertificate_privateCert
--- PASS: TestAccAWSAcmCertificate_privateCert (12.92s)
PASS
ok  	github.com/terraform-providers/terraform-provider-aws/aws	12.965s

@ghost ghost added size/M Managed by automation to categorize the size of a PR. documentation Introduces or discusses updates to documentation. service/acm Issues and PRs that pertain to the acm service. tests PRs: expanded test coverage. Issues: expanded coverage, enhancements to test infrastructure. labels Nov 30, 2018
@bflad bflad added the enhancement Requests to existing resources that expand the functionality or scope. label Nov 30, 2018
acmconn := meta.(*AWSClient).acmconn
params := &acm.RequestCertificateInput{
DomainName: aws.String(d.Get("domain_name").(string)),
ValidationMethod: aws.String(d.Get("validation_method").(string)),
IdempotencyToken: aws.String(strconv.Itoa(rand.Int())),
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering if using some sort of hash of the domain_name (that fits in 32 characters or less) would be more suitable.
That is, one might not want to generate multiple certificates for the same domain if somehow the API request fails and has to retry multiple times within the hour.

From the RequestCertificate API documentation:

Customer chosen string that can be used to distinguish between calls to RequestCertificate. Idempotency tokens time out after one hour. Therefore, if you call RequestCertificate multiple times with the same idempotency token within one hour, ACM recognizes that you are requesting only one certificate and will issue only one. If you change the idempotency token for each call, ACM recognizes that you are requesting multiple certificates.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we may want to hash domain name + SANs + private CA ARN if it's a private cert.

Copy link
Contributor

Choose a reason for hiding this comment

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

@micbase Good question.

I was wondering if the goal of the token is to distinguish two (or possibly more) of the same calls to the RequestCertificate API (i.e. with the exact same parameters) so that the same certificate ARN is returned to prevent creating a new certificate if the client was somehow not able to read the response (e.g. network issues).
That is whether changing one parameter (e.g. adding a SAN) is considered to a different "request" from AWS perspective even if you provide the same idempotency token.

So, I run the following command against two PCAs in a single region from a single account during the same hour and got some interesting results.

Requesting a certificate multiple times with the same token:

$ date && aws acm request-certificate --domain-name pr-6666.com --certificate-authority-arn arn:aws:acm-pca:region:1234:certificate-authority/1 --idempotency-token PR6666
Tue  5 Feb 2019 12:32:55 PST
{
    "CertificateArn": "arn:aws:acm:region:1234:certificate/78d5b732-88a8-49af-8929-f28359eb8b75"
}
$ date && aws acm request-certificate --domain-name pr-6666.com --certificate-authority-arn arn:aws:acm-pca:region:1234:certificate-authority/1 --idempotency-token PR6666
Tue  5 Feb 2019 12:33:01 PST
{
    "CertificateArn": "arn:aws:acm:region:1234:certificate/78d5b732-88a8-49af-8929-f28359eb8b75"
}

Then adding a SAN yet keeping the same token will generate a new certificate:

$ date && aws acm request-certificate --domain-name pr-6666.com --subject-alternative-names pr-6666.org --certificate-authority-arn arn:aws:acm-pca:region:1234:certificate-authority/1 --idempotency-token PR6666
Tue  5 Feb 2019 12:35:16 PST
{
    "CertificateArn": "arn:aws:acm:region:1234:certificate/b75395d9-d9fe-4249-8b30-c7ac4d1fbfd0"
}

$ date && aws acm request-certificate --domain-name pr-6666.com --subject-alternative-names pr-6666.org --certificate-authority-arn arn:aws:acm-pca:region:1234:certificate-authority/1 --idempotency-token PR6666
Tue  5 Feb 2019 12:35:32 PST
{
    "CertificateArn": "arn:aws:acm:region:1234:certificate/b75395d9-d9fe-4249-8b30-c7ac4d1fbfd0"
}

Then changing the certificate authority (same account, same region) somehow returns the same certificate (I consider that behaviour a bug):

$ date && aws acm request-certificate --domain-name pr-6666.com --subject-alternative-names pr-6666.org --certificate-authority-arn arn:aws:acm-pca:region:1234:certificate-authority/2 --idempotency-token PR6666
Tue  5 Feb 2019 12:36:05 PST
{
    "CertificateArn": "arn:aws:acm:region:1234:certificate/b75395d9-d9fe-4249-8b30-c7ac4d1fbfd0"
}

Then adding one more SAN generates a new certificate:

$ date && aws acm request-certificate --domain-name pr-6666.com --subject-alternative-names pr-6666.org pr-6666.net --certificate-authority-arn arn:aws:acm-pca:region:1234:certificate-authority/2 --idempotency-token PR6666
Tue  5 Feb 2019 12:39:03 PST
{
    "CertificateArn": "arn:aws:acm:region:1234:certificate/0d41f00a-25ce-492e-b6c4-1bb34b64da60"
}
$ date && aws acm request-certificate --domain-name pr-6666.com --subject-alternative-names pr-6666.org pr-6666.net --certificate-authority-arn arn:aws:acm-pca:region:1234:certificate-authority/2 --idempotency-token PR6666
Tue  5 Feb 2019 12:39:10 PST
{
    "CertificateArn": "arn:aws:acm:region:1234:certificate/0d41f00a-25ce-492e-b6c4-1bb34b64da60"
}

And switching back to the original certificate authority do keep the same certificate (again kind of unexpected behaviour IMHO):

$ date && aws acm request-certificate --domain-name pr-6666.com --subject-alternative-names pr-6666.org pr-6666.net --certificate-authority-arn arn:aws:acm-pca:region:1234:certificate-authority/1 --idempotency-token PR6666
Tue  5 Feb 2019 12:39:30 PST
{
    "CertificateArn": "arn:aws:acm:region:1234:certificate/0d41f00a-25ce-492e-b6c4-1bb34b64da60"
}

I would be keen to only have the domain name as part of the hash but it seems that adding the PCA ARN could work around that bug of getting the same certificate.
However this might be fixed eventually (if it is a bug and not a feature).

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is the exact problem I encountered, apologize for not expressing it clearly in my initial PR.
IMO, using two different PCAs should generate different certs, so I used IdempotencyToken to work it around. I'm not sure if that's a bug or feature, more like a bug to me.

if ok {
params.CertificateAuthorityArn = aws.String(caARN.(string))
} else {
params.ValidationMethod = aws.String(d.Get("validation_method").(string))
Copy link
Contributor

Choose a reason for hiding this comment

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

The changes made to move the validation_method argument from required to optional might require some extra validation to prevent calling the API for public certificate with the unsupported value NONE (if such an argument is not set).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@slaunay
Copy link
Contributor

slaunay commented Jan 31, 2019

@micbase Any chance you can resolve the conflicts and look at my comments?

If not I would be happy to take over by creating a separate PR based on your commits.
I'm really interested in that feature and your changes make a lot of sense to me.

@micbase
Copy link
Contributor Author

micbase commented Jan 31, 2019

@slaunay Sorry about the delay, will take a look this weekend.

@micbase
Copy link
Contributor Author

micbase commented Feb 6, 2019

@slaunay I updated the branch, please take a look when you have time.

@slaunay
Copy link
Contributor

slaunay commented Feb 7, 2019

I have reach to AWS about that token behaviour and they were able to reproduce it including with an invalid certificate authority ARN:

Additionally, if an idempotency-token is included then certificate manager is not verifying the certificate authority provided in the request if same idempotency token used in the same hour.

I have requested a private certificate and provided invalid certificate-authority ARN along with the same idempotency-token and I was issued the same certificate ARN without any error. This proves that certificate Manager is not verifying the certificate authority when same idempotency-token is used again in the same hour. In order to get clarity on this behavior, I have logged a request with my internal team and I will update you as soon as I hear from them.

They also mentioned the same workaround of using a different token when the certificate authority changes, I'll provide an update once they get back to me.

It would also be nice to have somebody with write access to the repo look at this PR now that is has no merge conflicts.

@bflad
Copy link
Contributor

bflad commented Feb 8, 2019

Hey folks 👋 We typically use something like resource.UniqueId() or some form of random string for ClientToken/IdempotencyToken to ensure we don't have length issues (although probably not an issue with the hashing) or cause unexpected behavior with operators explicitly calling terraform taint shortly after creating a resource. For the last point, does ACM correctly recreate the certificate if you create with an IdempotencyToken, delete it, then try to recreate with the same IdempotencyToken?

I guess more generally my question is whether its worth all the hassle trying to come up with the more complicated implementation (that may be potentially incorrect/buggy) for an atypical situation that we won't be able to easily test. For general networking issues, the AWS Go SDK should be automatically retrying with the provided IdempotencyToken in the original request.

@slaunay
Copy link
Contributor

slaunay commented Feb 15, 2019

Does ACM correctly recreate the certificate if you create with an IdempotencyToken, delete it, then try to recreate with the same IdempotencyToken?

It seems that the token is tied to the life cycle of the generated certificate as seen with the following test:

Issuing a new certificate twice using the same token:

$date && aws acm request-certificate --domain-name pr-6666.com --certificate-authority-arn arn:aws:acm-pca:region:1234:certificate-authority/1 --idempotency-token PR6666
Fri 15 Feb 2019 09:10:24 PST
{
    "CertificateArn": "arn:aws:acm:region:1234:certificate/cc030479-17a7-45a3-bdb5-bfcd454aa24f"
}
$ date && aws acm request-certificate --domain-name pr-6666.com --certificate-authority-arn arn:aws:acm-pca:region:1234:certificate-authority/1 --idempotency-token PR6666
Fri 15 Feb 2019 09:10:30 PST
{
    "CertificateArn": "arn:aws:acm:region:1234:certificate/cc030479-17a7-45a3-bdb5-bfcd454aa24f"
}

Deleting that certificate:

$ date && aws acm delete-certificate --certificate-arn arn:aws:acm:region:1234:certificate/cc030479-17a7-45a3-bdb5-bfcd454aa24f
Fri 15 Feb 2019 09:11:21 PST

Reissuing the "same" certificate within the same hour using the same token result in a different ARN:

$ date && aws acm request-certificate --domain-name pr-6666.com --certificate-authority-arn arn:aws:acm-pca:region:1234:certificate-authority/1 --idempotency-token PR6666
Fri 15 Feb 2019 09:11:28 PST
{
    "CertificateArn": "arn:aws:acm:region:1234:certificate/70f98a7d-c799-4c43-bdc8-4928300a3d53"
}
$ date && aws acm request-certificate --domain-name pr-6666.com --certificate-authority-arn arn:aws:acm-pca:region:1234:certificate-authority/1 --idempotency-token PR6666
Fri 15 Feb 2019 09:11:32 PST
{
    "CertificateArn": "arn:aws:acm:region:1234:certificate/70f98a7d-c799-4c43-bdc8-4928300a3d53"
}

AWS support got back to me and here is their response that confirm the bug mentioned above:

Firstly, I appreciate your feed back as it helped us to identify an issue in validation while requesting a private certificate using ACM PCA. Our team has confirmed that this isn't the expected behavior while making 'RequestCertificate' call for private certificates using idempotency-token.

Since our Internal team noticed the issue, this issue will be fixed by our service team. Being a support engineer I don't have visibility over the progress of this issue. Hence I will not be able to provide you an ETA regarding this issue.

Till this issue is fixed, please consider using the different idempotency tokens while making a request to issue private certificate from different private certificate authorities and I suggest you to keep an eye on our What's New page, Blog and RSS Feed for latest updates on AWS where we announce all new features when we release them.

For general networking issues, the AWS Go SDK should be automatically retrying with the provided IdempotencyToken in the original request.

True, the internal retry is a good first line of defence to avoid generating two certificates for the same domain during a single terraform apply.
Now, if somehow such retries would fail (e.g. loss of connectivity for a short period of time, terraform crashing or maybe double Ctrl+C by user) then it could be useful to make sure that the new terraform apply do not leave an "orphan" certificate in ACM behind.

I would personally lean towards using only the domain name to compute the idempotency token and explicitly documenting that, knowing that it would currently return the same certificate ARN when changing the PCA.

We could also expose the token to the end user as an attribute to set, maybe with a default to such hash for more flexibility but that seems convoluted to me.

What do you guys think make sense?

@micbase
Copy link
Contributor Author

micbase commented Feb 21, 2019

So when you using the same IdempotencyToken with different domain name, ACM will create new cert right? Why do we want to compute the token based on domain name?

@slaunay
Copy link
Contributor

slaunay commented Feb 22, 2019

I believe the crux of the idempotency token is to deduplicate the exact same request (i.e. same domain name requested plus other properties) in case the client is retrying to prevent issuing a new certificate.
So it makes sense to me that deduplication does not happen if you use a different domain but the same token as seen above.

Now for what to put in the token, I think it really depends on how bad you want to prevent a new certificate from getting issued:

  • when a retry is done by AWS SDK (will send the same request payload and therefore the same token you computed whatever that is)
  • when a retry is done submitting a new HTTP request from a new terraform apply process during the same hour (will send a new token you computed, likely different if you are using resource.UniqueId() but would be the same if you hash the domain name)

@slaunay
Copy link
Contributor

slaunay commented Mar 6, 2019

@bflad What approach would you recommend?

@slaunay
Copy link
Contributor

slaunay commented Apr 1, 2019

@bflad Do you have any suggestions on how to get this feature integrated into the AWS provider?

@aeschright aeschright requested a review from a team June 25, 2019 21:47
@mattburgess
Copy link
Collaborator

I have a very similar looking PR at #9288 which I obviously raised because my poor searching didn't find this one somehow! Interestingly, you don't appear to have hit the issue I did whereby I need to wait for the cert to be in ISSUED state, otherwise things would die horribly due to various fields not being populated until that happens. I wonder if ACM PCA has different performance in different regions such that your tests never hit that condition? Anyway, would be really good if someone could review this and mine please so that we can get support upstream.

Copy link
Contributor

@bflad bflad left a comment

Choose a reason for hiding this comment

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

This looks good, thanks, @micbase 🚀 The only minor changes on merge are changing IdempotencyToken to use resource.PrefixedUniqueId() which uses a monotonic counter to follow the conventions with the rest of the provider and having these new private certificates automatically retry while they sit in PENDING_VALIDATION where certificate details such as domain name are filled in by the API.

Output from acceptance testing:

--- PASS: TestAccAWSAcmCertificate_imported_IpAddress (10.34s)
--- PASS: TestAccAWSAcmCertificate_wildcard (13.56s)
--- PASS: TestAccAWSAcmCertificate_dnsValidation (15.16s)
--- PASS: TestAccAWSAcmCertificate_privateCert (15.19s)
--- PASS: TestAccAWSAcmCertificate_imported_DomainName (15.45s)
--- PASS: TestAccAWSAcmCertificate_rootAndWildcardSan (16.29s)
--- PASS: TestAccAWSAcmCertificate_root_TrailingPeriod (16.29s)
--- PASS: TestAccAWSAcmCertificate_root (16.31s)
--- PASS: TestAccAWSAcmCertificate_san_TrailingPeriod (17.01s)
--- PASS: TestAccAWSAcmCertificate_san_multiple (18.08s)
--- PASS: TestAccAWSAcmCertificate_disableCTLogging (18.53s)
--- PASS: TestAccAWSAcmCertificate_wildcardAndRootSan (18.66s)
--- PASS: TestAccAWSAcmCertificate_san_single (21.37s)
--- PASS: TestAccAWSAcmCertificate_emailValidation (23.08s)
--- PASS: TestAccAWSAcmCertificate_tags (33.48s)

@bflad bflad added this to the v2.23.0 milestone Aug 6, 2019
@bflad bflad merged commit d96d866 into hashicorp:master Aug 6, 2019
bflad added a commit that referenced this pull request Aug 6, 2019
@ghost
Copy link

ghost commented Aug 7, 2019

This has been released in version 2.23.0 of the Terraform AWS provider. Please see the Terraform documentation on provider versioning or reach out if you need any assistance upgrading.

For further feature requests or bug reports with this functionality, please create a new GitHub issue following the template for triage. Thanks!

@ghost
Copy link

ghost commented Nov 2, 2019

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. Thanks!

@ghost ghost locked and limited conversation to collaborators Nov 2, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
documentation Introduces or discusses updates to documentation. enhancement Requests to existing resources that expand the functionality or scope. service/acm Issues and PRs that pertain to the acm service. size/M Managed by automation to categorize the size of a PR. tests PRs: expanded test coverage. Issues: expanded coverage, enhancements to test infrastructure.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants