Merge pull request #4007 from edglynes/3904_v2_API

Update Aka issuer to use v2 API
This commit is contained in:
jetstack-bot
2021-05-21 12:34:25 +01:00
committed by GitHub
9 changed files with 906 additions and 719 deletions
+265
View File
@@ -417,6 +417,215 @@
================================================================================
================================================================================
= vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang licensed under: =
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
= vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/LICENSE e3fc50a88d0a364313df4b21ef20c29e
================================================================================
================================================================================
= vendor/github.com/aws/aws-sdk-go licensed under: =
@@ -11895,6 +12104,33 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================================================
================================================================================
= vendor/github.com/patrickmn/go-cache licensed under: =
Copyright (c) 2012-2017 Patrick Mylund Nielsen and the go-cache contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
= vendor/github.com/patrickmn/go-cache/LICENSE ad59632c93a4b0296b360ba542decb5e
================================================================================
================================================================================
= vendor/github.com/pavel-v-chernykh/keystore-go licensed under: =
@@ -13104,6 +13340,35 @@ SOFTWARE.
================================================================================
================================================================================
= vendor/github.com/sirupsen/logrus licensed under: =
The MIT License (MIT)
Copyright (c) 2014 Simon Eskildsen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
= vendor/github.com/sirupsen/logrus/LICENSE 8dadfef729c08ec4e631c4f6fc5d43a0
================================================================================
================================================================================
= vendor/github.com/spf13/cobra licensed under: =
+1
View File
@@ -15,6 +15,7 @@ require (
github.com/Azure/go-autorest/autorest/to v0.2.0
github.com/Azure/go-autorest/autorest/validation v0.1.0 // indirect
github.com/Venafi/vcert/v4 v4.13.1
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.0
github.com/aws/aws-sdk-go v1.34.30
github.com/cloudflare/cloudflare-go v0.13.2
github.com/cpu/goacmedns v0.0.3
+18 -1
View File
@@ -88,6 +88,8 @@ github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vaj
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.0 h1:NNsy0ugDFZbu6S11NlsR4Kmko9hJ00gv9U2JHbwpf3g=
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.0/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -454,6 +456,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/api v1.3.0 h1:HXNYlRkkM/t+Y/Yhxtwcy02dlYwIaoxzvxPnS+cqy78=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
@@ -669,6 +673,8 @@ github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
@@ -708,6 +714,8 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pavel-v-chernykh/keystore-go v2.1.0+incompatible h1:Jd6xfriVlJ6hWPvYOE0Ni0QWcNTLRehfGPFxr3eSL80=
github.com/pavel-v-chernykh/keystore-go v2.1.0+incompatible/go.mod h1:xlUlxe/2ItGlQyMTstqeDv9r3U4obH7xYd26TbDQutY=
github.com/pavel-v-chernykh/keystore-go/v4 v4.1.0 h1:xKxUVGoB9VJU+lgQLPN0KURjw+XCVVSpHfQEeyxk3zo=
@@ -863,6 +871,12 @@ github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/vektah/gqlparser v1.1.2 h1:ZsyLGn7/7jDNI+y4SEhI4yAxRChlv15pUHMjijT+e68=
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
@@ -1178,10 +1192,13 @@ gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.51.1 h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho=
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI=
+63 -2
View File
@@ -59,6 +59,15 @@ def go_repositories():
version = "v1.0.1",
)
go_repository(
name = "com_github_akamai_AkamaiOPEN-edgegrid-golang",
build_file_generation = "on",
build_file_proto_mode = "disable",
importpath = "github.com/akamai/AkamaiOPEN-edgegrid-golang",
sum = "h1:NNsy0ugDFZbu6S11NlsR4Kmko9hJ00gv9U2JHbwpf3g=",
version = "v1.1.0",
)
go_repository(
name = "com_github_alecthomas_template",
build_file_generation = "on",
@@ -1311,6 +1320,15 @@ def go_repositories():
sum = "h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI=",
version = "v1.9.5",
)
go_repository(
name = "com_github_h2non_parth",
build_file_generation = "on",
build_file_proto_mode = "disable",
importpath = "github.com/h2non/parth",
sum = "h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=",
version = "v0.0.0-20190131123155-b4df798d6542",
)
go_repository(
name = "com_github_hailocab_go_hostpool",
build_file_generation = "on",
@@ -2121,6 +2139,15 @@ def go_repositories():
sum = "h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=",
version = "v1.0.1",
)
go_repository(
name = "com_github_nbio_st",
build_file_generation = "on",
build_file_proto_mode = "disable",
importpath = "github.com/nbio/st",
sum = "h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=",
version = "v0.0.0-20140626010706-e9e8d9816f32",
)
go_repository(
name = "com_github_niemeyer_pretty",
build_file_generation = "on",
@@ -2865,6 +2892,31 @@ def go_repositories():
sum = "h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=",
version = "v1.0.0",
)
go_repository(
name = "com_github_xeipuuv_gojsonpointer",
build_file_generation = "on",
build_file_proto_mode = "disable",
importpath = "github.com/xeipuuv/gojsonpointer",
sum = "h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=",
version = "v0.0.0-20180127040702-4e3ac2762d5f",
)
go_repository(
name = "com_github_xeipuuv_gojsonreference",
build_file_generation = "on",
build_file_proto_mode = "disable",
importpath = "github.com/xeipuuv/gojsonreference",
sum = "h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=",
version = "v0.0.0-20180127040603-bd5ef7bd5415",
)
go_repository(
name = "com_github_xeipuuv_gojsonschema",
build_file_generation = "on",
build_file_proto_mode = "disable",
importpath = "github.com/xeipuuv/gojsonschema",
sum = "h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=",
version = "v1.2.0",
)
go_repository(
name = "com_github_xiang90_probing",
build_file_generation = "on",
@@ -3043,6 +3095,15 @@ def go_repositories():
sum = "h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs=",
version = "v1.2.3",
)
go_repository(
name = "in_gopkg_h2non_gock_v1",
build_file_generation = "on",
build_file_proto_mode = "disable",
importpath = "gopkg.in/h2non/gock.v1",
sum = "h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=",
version = "v1.0.15",
)
go_repository(
name = "in_gopkg_inf_v0",
build_file_generation = "on",
@@ -3056,8 +3117,8 @@ def go_repositories():
build_file_generation = "on",
build_file_proto_mode = "disable",
importpath = "gopkg.in/ini.v1",
sum = "h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=",
version = "v1.51.0",
sum = "h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho=",
version = "v1.51.1",
)
go_repository(
name = "in_gopkg_mgo_v2",
+5 -9
View File
@@ -2,16 +2,14 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"akamai.go",
"edgegridauth.go",
],
srcs = ["akamai.go"],
importpath = "github.com/jetstack/cert-manager/pkg/issuer/acme/dns/akamai",
visibility = ["//visibility:public"],
deps = [
"//pkg/issuer/acme/dns/util:go_default_library",
"//pkg/logs:go_default_library",
"//pkg/util:go_default_library",
"@com_github_akamai_AkamaiOPEN-edgegrid-golang//configdns-v2:go_default_library",
"@com_github_akamai_AkamaiOPEN-edgegrid-golang//edgegrid:go_default_library",
"@com_github_go_logr_logr//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
],
@@ -19,13 +17,11 @@ go_library(
go_test(
name = "go_default_test",
srcs = [
"akamai_test.go",
"edgegridauth_test.go",
],
srcs = ["akamai_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/issuer/acme/dns/util:go_default_library",
"@com_github_akamai_AkamaiOPEN-edgegrid-golang//configdns-v2:go_default_library",
"@com_github_stretchr_testify//assert:go_default_library",
],
)
+195 -202
View File
@@ -15,51 +15,76 @@ limitations under the License.
*/
// Package akamai implements a DNS provider for solving the DNS-01
// challenge using Akamai FastDNS.
// See https://developer.akamai.com/api/luna/config-dns/overview.html
// challenge using Akamai Edge DNS.
// See https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html
package akamai
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
dns "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v2"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid"
"github.com/go-logr/logr"
"github.com/pkg/errors"
"github.com/jetstack/cert-manager/pkg/issuer/acme/dns/util"
logf "github.com/jetstack/cert-manager/pkg/logs"
pkgutil "github.com/jetstack/cert-manager/pkg/util"
)
// Interface defined to enable mocking and required functions
type OpenEdgegridDNSService interface {
GetRecord(zone string, name string, record_type string) (*dns.RecordBody, error)
RecordSave(rec *dns.RecordBody, zone string) error
RecordUpdate(rec *dns.RecordBody, zone string) error
RecordDelete(rec *dns.RecordBody, zone string) error
}
type OpenDNSConfig struct {
config edgegrid.Config
}
// DNSProvider is an implementation of the acme.ChallengeProvider interface
type DNSProvider struct {
dns01Nameservers []string
// serviceConsumerDomain as issued by Akamai Luna Control Center.
// The ServiceConsumerDomain is the base URL.
serviceConsumerDomain string
auth *EdgeGridAuth
transport http.RoundTripper
dns01Nameservers []string
serviceConsumerDomain string
dnsclient OpenEdgegridDNSService
TTL int
findHostedDomainByFqdn func(string, []string) (string, error)
isNotFound func(error) bool
log logr.Logger
}
// NewDNSProvider returns a DNSProvider instance configured for Akamai.
func NewDNSProvider(serviceConsumerDomain, clientToken, clientSecret, accessToken string, dns01Nameservers []string) (*DNSProvider, error) {
return &DNSProvider{
dns01Nameservers,
serviceConsumerDomain,
NewEdgeGridAuth(clientToken, clientSecret, accessToken),
http.DefaultTransport,
findHostedDomainByFqdn,
logf.Log.WithName("akamai-dns"),
}, nil
// required Aka OpenEdgegrid creds + non empty dnsservers list
if serviceConsumerDomain == "" || clientToken == "" || clientSecret == "" || accessToken == "" || len(dns01Nameservers) < 1 {
return nil, fmt.Errorf("edgedns: Provider creation failed. Missing required arguments.")
}
dnsp := &DNSProvider{
dns01Nameservers: dns01Nameservers,
serviceConsumerDomain: serviceConsumerDomain,
dnsclient: &OpenDNSConfig{},
findHostedDomainByFqdn: findHostedDomainByFqdn,
isNotFound: isNotFound,
log: logf.Log.WithName("akamai-dns"),
TTL: 300,
}
dnsp.dnsclient.(*OpenDNSConfig).config = edgegrid.Config{
Host: serviceConsumerDomain,
ClientToken: clientToken,
ClientSecret: clientSecret,
AccessToken: accessToken,
MaxBody: 131072,
}
dns.Init(dnsp.dnsclient.(*OpenDNSConfig).config)
return dnsp, nil
}
func findHostedDomainByFqdn(fqdn string, ns []string) (string, error) {
@@ -71,218 +96,186 @@ func findHostedDomainByFqdn(fqdn string, ns []string) (string, error) {
return util.UnFqdn(zone), nil
}
// Present creates a TXT record to fulfil the dns-01 challenge
// Present creates/updates a TXT record to fulfill the dns-01 challenge.
func (a *DNSProvider) Present(domain, fqdn, value string) error {
return a.setTxtRecord(fqdn, &dns01Record{value, 60})
}
// CleanUp removes the TXT record matching the specified parameters
func (a *DNSProvider) CleanUp(domain, fqdn, value string) error {
return a.setTxtRecord(fqdn, nil)
}
logf.V(logf.DebugLevel).Infof("entering Present. domain: %s, fqdn: %s, value: %s", domain, fqdn, value)
type dns01Record struct {
value string
ttl int
}
func (a *DNSProvider) setTxtRecord(fqdn string, dns01Record *dns01Record) error {
hostedDomain, err := a.findHostedDomainByFqdn(fqdn, a.dns01Nameservers)
if err != nil {
return errors.Wrapf(err, "failed to determine hosted domain for %q", fqdn)
}
zoneData, err := a.loadZoneData(hostedDomain)
if err != nil {
return errors.Wrapf(err, "failed to load zone data for %q", hostedDomain)
return errors.Wrapf(err, "edgedns: failed to determine hosted domain for %q", fqdn)
}
hostedDomain = util.UnFqdn(hostedDomain)
logf.V(logf.DebugLevel).Infof("hostedDomain: %s", hostedDomain)
recordName, err := makeTxtRecordName(fqdn, hostedDomain)
if err != nil {
return errors.Wrapf(err, "failed to create TXT record name")
return errors.Wrapf(err, "edgedns: failed to create TXT record name")
}
logf.V(logf.DebugLevel).Infof("recordName: %s", recordName)
record, err := a.dnsclient.GetRecord(hostedDomain, recordName, "TXT")
if err != nil && !a.isNotFound(err) {
return errors.Wrapf(err, "edgedns: failed to retrieve TXT record")
}
if updated, err := zoneData.setTxtRecord(recordName, dns01Record); !updated || err != nil {
if err != nil {
return errors.Wrapf(err, "failed to set TXT record in %q", hostedDomain)
if err == nil && record == nil {
return fmt.Errorf("edgedns: unknown error")
}
if record != nil {
logf.V(logf.InfoLevel).Infof("edgedns: TXT record already exists. Updating target")
if containsValue(record.Target, value) {
// have a record and have entry already
return nil
}
return errors.Errorf("no %q TXT record found in %q", recordName, hostedDomain)
record.Target = append(record.Target, `"`+value+`"`)
record.TTL = a.TTL
err = a.dnsclient.RecordUpdate(record, hostedDomain)
if err != nil {
return errors.Wrapf(err, "edgedns: failed to update TXT record")
}
return nil
}
newSerial, err := zoneData.incSoaSerial()
record = &dns.RecordBody{
Name: recordName,
RecordType: "TXT",
TTL: a.TTL,
Target: []string{`"` + value + `"`},
}
err = a.dnsclient.RecordSave(record, hostedDomain)
if err != nil {
return errors.Wrapf(err, "failed to increment SOA serial for %q", hostedDomain)
return errors.Wrapf(err, "edgedns: failed to create TXT record")
}
if err := a.saveZoneData(hostedDomain, zoneData); err != nil {
return errors.Wrapf(err, "failed to save zone data for %q", hostedDomain)
}
logf.V(logf.DebugLevel).Infof("Updated Akamai TXT record for %q on %q using SOA serial of %d", recordName, hostedDomain, newSerial)
return nil
}
// CleanUp removes/updates the TXT record matching the specified parameters.
func (a *DNSProvider) CleanUp(domain, fqdn, value string) error {
logf.V(logf.DebugLevel).Infof("entering CleanUp. domain: %s, fqdn: %s, value: %s", domain, fqdn, value)
hostedDomain, err := a.findHostedDomainByFqdn(fqdn, a.dns01Nameservers)
if err != nil {
return errors.Wrapf(err, "edgedns: failed to determine hosted domain for %q", fqdn)
}
hostedDomain = util.UnFqdn(hostedDomain)
logf.V(logf.DebugLevel).Infof("hostedDomain: %s", hostedDomain)
recordName, err := makeTxtRecordName(fqdn, hostedDomain)
if err != nil {
return errors.Wrapf(err, "edgedns: failed to create TXT record name")
}
logf.V(logf.DebugLevel).Infof("recordName: %s", recordName)
existingRec, err := a.dnsclient.GetRecord(hostedDomain, recordName, "TXT")
if err != nil {
if a.isNotFound(err) {
return nil
}
return errors.Wrapf(err, "edgedns: failed to retrieve TXT record")
}
if existingRec == nil {
return fmt.Errorf("edgedns: unknown failure")
}
if len(existingRec.Target) == 0 {
return fmt.Errorf("edgedns: TXT record is invalid")
}
if !containsValue(existingRec.Target, value) {
return nil
}
var newRData []string
for _, val := range existingRec.Target {
tval := strings.Trim(val, `"`)
if tval == value {
continue
}
newRData = append(newRData, val)
}
if len(newRData) > 0 {
existingRec.Target = newRData
logf.V(logf.DebugLevel).Infof("updating Akamai TXT record: %s, data: %s", existingRec.Name, newRData)
err = a.dnsclient.RecordUpdate(existingRec, hostedDomain)
if err != nil {
return errors.Wrapf(err, "edgedns: TXT record update failed")
}
return nil
}
logf.V(logf.DebugLevel).Infof("deleting Akamai TXT record %s", existingRec.Name)
err = a.dnsclient.RecordDelete(existingRec, hostedDomain)
if err != nil {
return errors.Wrapf(err, "edgedns: TXT record delete failed")
}
return nil
}
func containsValue(values []string, value string) bool {
for _, val := range values {
if strings.Trim(val, `"`) == value {
return true
}
}
return false
}
func isNotFound(err error) bool {
if err == nil {
return false
}
_, ok := err.(*dns.RecordError)
if ok {
return true
}
return false
}
func makeTxtRecordName(fqdn, hostedDomain string) (string, error) {
if !strings.HasSuffix(fqdn, "."+hostedDomain+".") {
recName := util.UnFqdn(fqdn)
if !strings.HasSuffix(recName, hostedDomain) {
return "", errors.Errorf("fqdn %q is not part of %q", fqdn, hostedDomain)
}
return fqdn[0 : len(fqdn)-len(hostedDomain)-2], nil
return recName, nil
}
func (a *DNSProvider) urlForDomain(domain string) string {
return fmt.Sprintf("https://%s/config-dns/v1/zones/%s", a.serviceConsumerDomain, domain)
func (o OpenDNSConfig) GetRecord(zone string, name string, record_type string) (*dns.RecordBody, error) {
dns.Config = o.config
return dns.GetRecord(zone, name, record_type)
}
func (a *DNSProvider) loadZoneData(domain string) (zoneData, error) {
url := a.urlForDomain(domain)
req, err := http.NewRequest(http.MethodGet, url, http.NoBody)
if err != nil {
return nil, errors.Wrap(err, "failed to create HTTP request")
}
func (o OpenDNSConfig) RecordSave(rec *dns.RecordBody, zone string) error {
responsePayload, err := a.makeRequest(req)
if err != nil {
return nil, err
}
var zoneData map[string]interface{}
err = json.NewDecoder(bytes.NewReader(responsePayload)).Decode(&zoneData)
if err != nil {
return nil, errors.Wrap(err, "failed to decode Akamai OPEN API response")
}
return zoneData, nil
return rec.Save(zone)
}
func (a *DNSProvider) saveZoneData(domain string, data zoneData) error {
body, err := json.Marshal(data)
if err != nil {
return errors.Wrap(err, "failed to encode zone data")
}
func (o OpenDNSConfig) RecordUpdate(rec *dns.RecordBody, zone string) error {
url := a.urlForDomain(domain)
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(body))
if err != nil {
return errors.Wrap(err, "failed to create HTTP request")
}
req.Header.Set("Content-Type", "application/json")
if _, err := a.makeRequest(req); err != nil {
return err
}
return nil
return rec.Update(zone)
}
func (a *DNSProvider) makeRequest(req *http.Request) ([]byte, error) {
req.Header.Set("User-Agent", pkgutil.CertManagerUserAgent)
func (o OpenDNSConfig) RecordDelete(rec *dns.RecordBody, zone string) error {
if err := a.auth.SignRequest(req); err != nil {
return nil, errors.Wrap(err, "failed to sign HTTP request")
}
client := http.Client{
Transport: a.transport,
Timeout: 30 * time.Second,
}
resp, err := client.Do(req)
if err != nil {
return nil, errors.Wrap(err, "error querying Akamai OPEN API")
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNoContent {
return nil, nil
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected response from Akamai OPEN API: status_code: %d, status: %s", resp.StatusCode, resp.Status)
}
responsePayload, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrap(err, "failed to read response payload")
}
return responsePayload, nil
}
type zoneData map[string]interface{}
func (z zoneData) setTxtRecord(name string, dns01Record *dns01Record) (bool, error) {
zone, ok := z["zone"].(map[string]interface{})
if !ok {
return false, errors.New("failed to retrieve zone from zone data")
}
var txtRecords []interface{}
if txtNode, ok := zone["txt"]; ok {
if txtRecords, ok = txtNode.([]interface{}); !ok {
return false, errors.New("failed to retrieve TXT records from zone data")
}
}
if dns01Record == nil {
if txtRecords = deleteRecord(txtRecords, name); txtRecords == nil {
return false, nil
}
} else {
txtRecords = updateRecord(txtRecords, name, map[string]interface{}{
"name": name,
"ttl": dns01Record.ttl,
"active": true,
"target": dns01Record.value,
})
}
if len(txtRecords) < 1 {
delete(zone, "txt")
} else {
zone["txt"] = txtRecords
}
return true, nil
}
func (z zoneData) incSoaSerial() (uint64, error) {
soa, ok := z["zone"].(map[string]interface{})["soa"].(map[string]interface{})
if !ok {
return 0, errors.New("failed to retrieve SOA record from zone data")
}
serial, ok := soa["serial"].(float64)
if !ok {
return 0, errors.New("failed to retrieve SOA serial from zone data")
}
newSerial := uint64(serial) + 1
soa["serial"] = newSerial
return newSerial, nil
}
func deleteRecord(records []interface{}, name string) []interface{} {
for pos := range records {
if recordName, ok := records[pos].(map[string]interface{})["name"]; ok && recordName == name {
return append(records[:pos], records[pos+1:]...)
}
}
return nil
}
func updateRecord(records []interface{}, name string, record map[string]interface{}) []interface{} {
for pos := range records {
if records[pos].(map[string]interface{})["name"] == name {
records[pos] = record
return records
}
}
return append(records, record)
return rec.Delete(zone)
}
+359 -128
View File
@@ -17,164 +17,395 @@ limitations under the License.
package akamai
import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"strings"
"testing"
"fmt"
"reflect"
dns "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v2"
"github.com/jetstack/cert-manager/pkg/issuer/acme/dns/util"
"github.com/stretchr/testify/assert"
)
const sampleZoneData = `{
"token": "a184671d5307a388180fbf7f11dbdf46",
"zone": {
"name": "example.com",
"soa": {
"contact": "hostmaster.akamai.com.",
"expire": 604800,
"minimum": 180,
"originserver": "use4.akamai.com.",
"refresh": 900,
"retry": 300,
"serial": 1271354824,
"ttl": 900
},
"ns": [
{
"active": true,
"name": "",
"target": "use4.akam.net.",
"ttl": 3600
},
{
"active": true,
"name": "",
"target": "use3.akam.net.",
"ttl": 3600
}
]
}
}`
func testRecordBodyData() *dns.RecordBody {
const sampleZoneDataWithTxt = `{
"token": "a184671d5307a388180fbf7f11dbdf46",
"zone": {
"name": "example.com",
"soa": {
"contact": "hostmaster.akamai.com.",
"expire": 604800,
"minimum": 180,
"originserver": "use4.akamai.com.",
"refresh": 900,
"retry": 300,
"serial": 1271354825,
"ttl": 900
},
"ns": [
{
"active": true,
"name": "",
"target": "use4.akam.net.",
"ttl": 3600
},
{
"active": true,
"name": "",
"target": "use3.akam.net.",
"ttl": 3600
}
],
"txt": [
{
"active": true,
"name" :"_acme-challenge.test",
"target": "dns01-key",
"ttl": 60
}
]
}
}`
type httpResponder func(req *http.Request) (*http.Response, error)
func (r httpResponder) RoundTrip(req *http.Request) (*http.Response, error) {
return r(req)
return &dns.RecordBody{
Name: "_acme-challenge.test.example.com",
RecordType: "TXT",
Target: []string{`"` + "dns01-key" + `"`},
TTL: 300,
}
}
func TestPresent(t *testing.T) {
func testRecordBodyDataExist() *dns.RecordBody {
return &dns.RecordBody{
Name: "_acme-challenge.test.example.com",
RecordType: "TXT",
Target: []string{`"` + "dns01-key" + `"`, `"` + "dns01-key-stub" + `"`},
TTL: 300,
}
}
// OpenEdggrid DNS Stub
type StubOpenDNSConfig struct {
FuncOutput map[string]interface{}
FuncErrors map[string]error
}
//
func findStubHostedDomainByFqdn(fqdn string, ns []string) (string, error) {
return "test.example.com", nil
}
func stubIsNotFoundTrue(err error) bool {
return true
}
func stubIsNotFoundFalse(err error) bool {
return false
}
// TestNewDNSProvider performs sanity check on provider init
func TestNewDNSProvider(t *testing.T) {
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
assert.NoError(t, err)
// samplee couple important fields
assert.Equal(t, akamai.serviceConsumerDomain, "akamai.example.com")
assert.Equal(t, fmt.Sprintf("%T", akamai.dnsclient), "*akamai.OpenDNSConfig")
}
// TestPresentBasicFlow tests basic flow, e.g. no record exists.
func TestPresentBasicFlow(t *testing.T) {
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
assert.NoError(t, err)
var response []byte
mockTransport(t, akamai, "example.com", sampleZoneData, &response)
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
akamai.isNotFound = stubIsNotFoundTrue
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = nil
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["RecordSave"] = testRecordBodyData()
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
assert.NoError(t, akamai.Present("test.example.com", "_acme-challenge.test.example.com.", "dns01-key"))
var expected, actual map[string]interface{}
assert.NoError(t, json.Unmarshal([]byte(sampleZoneDataWithTxt), &expected))
assert.NoError(t, json.Unmarshal(response, &actual))
assert.EqualValues(t, expected, actual)
}
func TestCleanUp(t *testing.T) {
// TestPresentExists tests flow with existing record.
func TestPresentExists(t *testing.T) {
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
assert.NoError(t, err)
var response []byte
mockTransport(t, akamai, "example.com", sampleZoneDataWithTxt, &response)
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
akamai.isNotFound = stubIsNotFoundFalse // ignored for this flow ...
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = testRecordBodyData()
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["RecordUpdate"] = testRecordBodyDataExist()
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
assert.NoError(t, akamai.Present("test.example.com", "_acme-challenge.test.example.com.", "dns01-key-stub"))
}
// TestPresentValueExists tests flow with existing record.
func TestPresentValueExists(t *testing.T) {
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
assert.NoError(t, err)
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
akamai.isNotFound = stubIsNotFoundFalse // ignored for this flow ...
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = testRecordBodyData()
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
assert.NoError(t, akamai.Present("test.example.com", "_acme-challenge.test.example.com.", "dns01-key"))
}
func TestPresentFailGetRecord(t *testing.T) {
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
assert.NoError(t, err)
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
akamai.isNotFound = stubIsNotFoundFalse
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = nil
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["GetRecord"] = fmt.Errorf("Failed Get Test")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
assert.Error(t, akamai.Present("test.example.com", "_acme-challenge.test.example.com.", "dns01-key"))
}
func TestPresentFailSaveRecord(t *testing.T) {
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
assert.NoError(t, err)
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
akamai.isNotFound = stubIsNotFoundTrue
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = nil
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save fail")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
assert.Error(t, akamai.Present("test.example.com", "_acme-challenge.test.example.com.", "dns01-key"))
}
func TestPresentFailUpdateRecord(t *testing.T) {
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
assert.NoError(t, err)
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
akamai.isNotFound = stubIsNotFoundFalse // ignored for this flow ...
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = testRecordBodyData()
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["RecordUpdate"] = testRecordBodyDataExist()
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update failed")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
assert.Error(t, akamai.Present("test.example.com", "_acme-challenge.test.example.com.", "dns01-key-stub"))
}
// TestCleanUpBasicFlow tests flow with existing record.
func TestCleanUpBasicFlow(t *testing.T) {
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
assert.NoError(t, err)
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
akamai.isNotFound = stubIsNotFoundFalse // ignored for this flow ...
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = testRecordBodyData()
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["RecordDelete"] = testRecordBodyData()
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
assert.NoError(t, akamai.CleanUp("test.example.com", "_acme-challenge.test.example.com.", "dns01-key"))
var expected, actual map[string]interface{}
assert.NoError(t, json.Unmarshal([]byte(sampleZoneData), &expected))
expected["zone"].(map[string]interface{})["soa"].(map[string]interface{})["serial"] = 1271354826.
assert.NoError(t, json.Unmarshal(response, &actual))
assert.EqualValues(t, expected, actual)
}
func mockTransport(t *testing.T, akamai *DNSProvider, domain, data string, response *[]byte) {
akamai.transport = httpResponder(func(req *http.Request) (*http.Response, error) {
defer req.Body.Close()
// TestPresentExists tests flow with existing record.
func TestCleanUpExists(t *testing.T) {
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
assert.NoError(t, err)
if req.URL.String() != "https://akamai.example.com/config-dns/v1/zones/"+domain {
return &http.Response{
StatusCode: http.StatusNotFound,
Body: http.NoBody,
}, nil
}
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
akamai.isNotFound = stubIsNotFoundFalse // ignored for this flow ...
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = testRecordBodyData()
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["RecordUpdate"] = testRecordBodyDataExist()
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
if req.Method == http.MethodGet {
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader([]byte(data))),
}, nil
}
assert.NoError(t, akamai.CleanUp("test.example.com", "_acme-challenge.test.example.com.", "dns01-key-stub"))
if req.Method == http.MethodPost {
if req.Header.Get("Content-Type") != "application/json" {
t.Fatalf("unsupported Content Type: %v", req.Header.Get("Content-Type"))
}
}
var err error
*response, err = ioutil.ReadAll(req.Body)
assert.NoError(t, err)
// TestCleanUpExistsNoValue tests flow with existing record.
func TestCleanUpExistsNoValue(t *testing.T) {
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
assert.NoError(t, err)
return &http.Response{
StatusCode: http.StatusNoContent,
Body: http.NoBody,
}, nil
}
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
akamai.isNotFound = stubIsNotFoundFalse // ignored for this flow ...
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = testRecordBodyData()
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
t.Fatalf("unexpected method: %v", req.Method)
return nil, nil
})
akamai.findHostedDomainByFqdn = func(fqdn string, _ []string) (string, error) {
if !strings.HasSuffix(fqdn, domain+".") {
t.Fatalf("unexpected fqdn: %s", fqdn)
}
return domain, nil
assert.NoError(t, akamai.CleanUp("test.example.com", "_acme-challenge.test.example.com.", "dns01-key-stub"))
}
// TestCleanUpNoRecord tests flow with no existing record.
func TestCleanUpNoRecord(t *testing.T) {
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
assert.NoError(t, err)
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
akamai.isNotFound = stubIsNotFoundTrue // ignored for this flow ...
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = nil
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
assert.NoError(t, akamai.CleanUp("test.example.com", "_acme-challenge.test.example.com.", "dns01"))
}
func TestCleanUpFailGetRecord(t *testing.T) {
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
assert.NoError(t, err)
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
akamai.isNotFound = stubIsNotFoundFalse
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = nil
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["GetRecord"] = fmt.Errorf("Failed Get Record")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
assert.Error(t, akamai.CleanUp("test.example.com", "_acme-challenge.test.example.com.", "dns01-key"))
}
func TestCleanUpFailUpdateRecord(t *testing.T) {
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
assert.NoError(t, err)
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
akamai.isNotFound = stubIsNotFoundFalse // ignored for this flow ...
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = testRecordBodyDataExist()
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["RecordUpdate"] = testRecordBodyData()
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update failed")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete not expected")
assert.Error(t, akamai.CleanUp("test.example.com", "_acme-challenge.test.example.com.", "dns01-key-stub"))
}
func TestCleanUpFailDeleteRecord(t *testing.T) {
akamai, err := NewDNSProvider("akamai.example.com", "token", "secret", "access-token", util.RecursiveNameservers)
assert.NoError(t, err)
akamai.findHostedDomainByFqdn = findStubHostedDomainByFqdn
akamai.isNotFound = stubIsNotFoundFalse // ignored for this flow ...
akamai.dnsclient = &StubOpenDNSConfig{FuncOutput: map[string]interface{}{}, FuncErrors: map[string]error{}}
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["GetRecord"] = testRecordBodyData()
akamai.dnsclient.(*StubOpenDNSConfig).FuncOutput["RecordDelete"] = testRecordBodyData()
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordSave"] = fmt.Errorf("Save not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordUpdate"] = fmt.Errorf("Update not expected")
akamai.dnsclient.(*StubOpenDNSConfig).FuncErrors["RecordDelete"] = fmt.Errorf("Delete failed")
assert.Error(t, akamai.CleanUp("test.example.com", "_acme-challenge.test.example.com.", "dns01-key"))
}
// Stub Get Record
func (o StubOpenDNSConfig) GetRecord(zone string, name string, record_type string) (*dns.RecordBody, error) {
var rec *dns.RecordBody
err, ok := o.FuncErrors["GetRecord"]
if ok {
return nil, err
}
exp, ok := o.FuncOutput["GetRecord"]
if ok {
if exp == nil {
return nil, fmt.Errorf("GetRecord: Unexpected nil")
}
rec = exp.(*dns.RecordBody)
// comare passed with expected
if name != rec.Name {
return nil, fmt.Errorf("GetRecord: expected/actual Name don't match")
}
if record_type != rec.RecordType {
return nil, fmt.Errorf("GetRecord: expected/actual Record Type don't match")
}
}
return rec, nil
}
func (o StubOpenDNSConfig) RecordSave(rec *dns.RecordBody, zone string) error {
exp, ok := o.FuncOutput["RecordSave"]
if ok {
// comare passed with expected
if rec.Name != exp.(*dns.RecordBody).Name {
return fmt.Errorf("RecordSave: expected/actual Name don't match")
}
if rec.RecordType != exp.(*dns.RecordBody).RecordType {
return fmt.Errorf("RecordSave: expected/actual Record Type don't match")
}
if !reflect.DeepEqual(rec.Target, exp.(*dns.RecordBody).Target) {
return fmt.Errorf("RecordSave: expected/actual Target don't match")
}
if rec.TTL != exp.(*dns.RecordBody).TTL {
return fmt.Errorf("RecordSave: expected/actual TTL don't match")
}
}
err, ok := o.FuncErrors["RecordSave"]
if ok {
return err
}
return nil
}
func (o StubOpenDNSConfig) RecordUpdate(rec *dns.RecordBody, zone string) error {
exp, ok := o.FuncOutput["RecordUpdate"]
if ok {
// comare passed with expected
if rec.Name != exp.(*dns.RecordBody).Name {
return fmt.Errorf("RecordUpdate: expected/actual Name don't match")
}
if rec.RecordType != exp.(*dns.RecordBody).RecordType {
return fmt.Errorf("RecordUpdate: expected/actual Record Type don't match")
}
if !reflect.DeepEqual(rec.Target, exp.(*dns.RecordBody).Target) {
return fmt.Errorf("RecordUpdate: expected/actual Target don't match")
}
if rec.TTL != exp.(*dns.RecordBody).TTL {
return fmt.Errorf("RecordUpdate: expected/actual TTL don't match")
}
}
err, ok := o.FuncErrors["RecordUpdate"]
if ok {
return err
}
return nil
}
func (o StubOpenDNSConfig) RecordDelete(rec *dns.RecordBody, zone string) error {
exp, ok := o.FuncOutput["RecordDelete"]
if ok {
// comare passed with expected
if rec.Name != exp.(*dns.RecordBody).Name {
return fmt.Errorf("RecordDelete: expected/actual Name don't match")
}
if rec.RecordType != exp.(*dns.RecordBody).RecordType {
return fmt.Errorf("RecordDelete: expected/actual Record Type don't match")
}
if !reflect.DeepEqual(rec.Target, exp.(*dns.RecordBody).Target) {
return fmt.Errorf("RecordDelete: expected/actual Target don't match")
}
if rec.TTL != exp.(*dns.RecordBody).TTL {
return fmt.Errorf("RecordDelete: expected/actual TTL don't match")
}
}
err, ok := o.FuncErrors["RecordDelete"]
if ok {
return err
}
return nil
}
-280
View File
@@ -1,280 +0,0 @@
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package akamai
import (
"bytes"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"sort"
"strings"
"time"
"unicode"
)
// EdgeGridAuth holds all values required to perform Akamai API Client Authentication.
// See https://developer.akamai.com/introduction/Client_Auth.html.
type EdgeGridAuth struct {
ClientToken string
ClientSecret string
AccessToken string
HeadersToSign []string
MaxBody int
now func() time.Time
createNonce func() (string, error)
}
type signingData struct {
timestamp string
authHeader string
dataToSign string
}
// edgeGridAuthTimeFormat is used for timestamps in request signatures.
const edgeGridAuthTimeFormat = "20060102T15:04:05-0700" // yyyyMMddTHH:mm:ss+0000
const NoMaxBody = -1
// NewEdgeGridAuth returns a new request signer for Akamai EdgeGrid
func NewEdgeGridAuth(clientToken, clientSecret, accessToken string, headersToSign ...string) *EdgeGridAuth {
return &EdgeGridAuth{
ClientToken: clientToken,
ClientSecret: clientSecret,
AccessToken: accessToken,
HeadersToSign: headersToSign,
MaxBody: NoMaxBody,
now: time.Now,
createNonce: createRandomNonce,
}
}
// SignRequest calculates the signature for Akamai Open API and adds it as the Authorization header.
// The Authorization header starts with the signing algorithm moniker (name of the algorithm) used to sign the request.
// The moniker below identifies EdgeGrid V1, hash message authentication code, SHA256 as the hash standard.
// This moniker is then followed by a space and an ordered list of name value pairs with each field separated by a semicolon.
func (e *EdgeGridAuth) SignRequest(req *http.Request) error {
signingData, err := e.signingData(req)
if err != nil {
return err
}
req.Header.Set("Authorization", fmt.Sprintf(
"%ssignature=%s",
signingData.authHeader,
e.calculateRequestSignature(signingData)))
return nil
}
func (e *EdgeGridAuth) calculateRequestSignature(signingData *signingData) string {
return computeSignature(
signingData.dataToSign,
e.signingKey(signingData.timestamp))
}
func (e *EdgeGridAuth) signingData(req *http.Request) (*signingData, error) {
nonce, err := e.createNonce()
if err != nil {
return nil, err
}
timestamp := e.now().UTC().Format(edgeGridAuthTimeFormat)
authHeader := fmt.Sprintf("EG1-HMAC-SHA256 client_token=%s;access_token=%s;timestamp=%s;nonce=%s;",
e.ClientToken,
e.AccessToken,
timestamp,
nonce)
return &signingData{
timestamp: timestamp,
authHeader: authHeader,
dataToSign: e.dataToSign(req, authHeader),
}, nil
}
// dataToSign includes the information from the HTTP request that is relevant to ensuring that the request is authentic.
// This data set comprised of the request data combined with the authorization header value (excluding the signature field,
// but including the ; right before the signature field).
func (e *EdgeGridAuth) dataToSign(req *http.Request, authHeader string) string {
var buffer bytes.Buffer
buffer.WriteString(req.Method)
buffer.WriteRune('\t')
buffer.WriteString(req.URL.Scheme)
buffer.WriteRune('\t')
buffer.WriteString(req.URL.Host)
buffer.WriteRune('\t')
buffer.WriteString(relativeURL(req.URL))
buffer.WriteRune('\t')
buffer.WriteString(e.canonicalizedHeaders(req))
buffer.WriteRune('\t')
buffer.WriteString(e.computeBodyHash(req))
buffer.WriteRune('\t')
buffer.WriteString(authHeader)
return buffer.String()
}
// signingKey is derived from the client secret.
// The signing key is computed as the base64 encoding of the SHA256 HMAC of the timestamp string
// (the field value included in the HTTP authorization header described above) with the client secret as the key.
func (e *EdgeGridAuth) signingKey(timestamp string) string {
return computeSignature(timestamp, e.ClientSecret)
}
// relativeURL is the part of the URL that starts from the root path and includes the query string, with the handling of following special cases:
// If the path is null or empty, set it to / (forward-slash).
// If the path does not start with /, add / to the beginning.
func relativeURL(url *url.URL) string {
relativeURL := url.Path
if relativeURL == "" {
return "/"
}
if relativeURL[0] != '/' {
relativeURL = "/" + relativeURL
}
if url.RawQuery != "" {
relativeURL += "?"
relativeURL += url.RawQuery
}
return relativeURL
}
// computeBodyHash returns the base64-encoded SHA256 hash of the POST body.
// For any other request methods, this field is empty. But the tac separator (\t) must be included.
// The size of the POST body must be less than or equal to the value specified by the service.
// Any request that does not meet this criteria SHOULD be rejected during the signing process,
// as the request will be rejected by EdgeGrid.
func (e *EdgeGridAuth) computeBodyHash(req *http.Request) string {
if req.Body != nil {
bodyBytes, _ := ioutil.ReadAll(req.Body)
req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
if req.Method == http.MethodPost && len(bodyBytes) > 0 {
dataToHash := bodyBytes
if e.MaxBody != NoMaxBody && len(dataToHash) > e.MaxBody {
dataToHash = dataToHash[0:e.MaxBody]
}
sha256Sum := sha256.Sum256(dataToHash)
return base64.StdEncoding.EncodeToString(sha256Sum[:])
}
}
return ""
}
// canonicalizedHeaders returns the request headers as a canonicalized string.
//
// The protocol does not support multiple request headers with the same header name.
// Such requests SHOULD be rejected during the signing process. Otherwise, EdgeGrid
// will not produce the intended results by rejecting such requests or removing all
// (but one) duplicated headers.
//
// Header names are case-insensitive per rfc2616.
//
// For each entry in the list of headers designated by the service provider to include
// in the signature in the specified order, the canonicalization of the request header
// is done as follows:
//
// Get the first header value for the name.
// Trim the leading and trailing white spaces.
// Replace all repeated white spaces with a single space.
// Concatenate the name:value pairs with the tab (\t) separator (name field is all in lower case).
// Terminate the headers with another tab (\t) separator.
//
// NOTE: The canonicalized data is used for creating the signature only, as this step
// might alter the header value. If a header in the list is not present in the request,
// or the header value is empty, nothing for that header, neither the name nor the tab
// separator, may be included.
func (e *EdgeGridAuth) canonicalizedHeaders(req *http.Request) string {
if len(e.HeadersToSign) < 1 {
return ""
}
var headerNamesToSign []string
for headerName := range req.Header {
for _, sign := range e.HeadersToSign {
if strings.EqualFold(sign, headerName) {
headerNamesToSign = append(headerNamesToSign, headerName)
break
}
}
}
if len(headerNamesToSign) < 1 {
return ""
}
sort.Strings(headerNamesToSign)
var buffer bytes.Buffer
for _, headerName := range headerNamesToSign {
for _, c := range headerName {
buffer.WriteRune(unicode.ToLower(c))
}
buffer.WriteRune(':')
white := false
empty := true
for _, c := range req.Header.Get(headerName) {
if unicode.IsSpace(c) {
white = true
} else {
if white && !empty {
buffer.WriteRune(' ')
}
buffer.WriteRune(unicode.ToLower(c))
empty = false
white = false
}
}
buffer.WriteRune('\t')
}
return buffer.String()
}
// calculateSignature is the base64-encoding of the SHA256 HMAC of the data to sign with the signing key.
func computeSignature(message string, secret string) string {
key := []byte(secret)
h := hmac.New(sha256.New, key)
h.Write([]byte(message))
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
func createRandomNonce() (string, error) {
bytes := make([]byte, 18)
_, err := rand.Read(bytes)
if err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(bytes), nil
}
@@ -1,97 +0,0 @@
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package akamai
import (
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestDataToSign(t *testing.T) {
req, err := http.NewRequest(
http.MethodGet,
"https://akaa-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx.luna.akamaiapis.net/diagnostic-tools/v1/locations",
http.NoBody)
assert.NoError(t, err)
auth := NewEdgeGridAuth("ClientToken", "ClientSecret", "AccessToken")
auth.now = func() time.Time {
return time.Unix(1396461906, 0) // 20140402T18:05:06Z
}
auth.createNonce = func() (string, error) {
return "185f94eb-537c-4c01-b8cc-2fa5a06aee7f", nil
}
data, err := auth.signingData(req)
assert.NoError(t, err)
expected := "GET" +
"\thttps" +
"\takaa-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx.luna.akamaiapis.net" +
"\t/diagnostic-tools/v1/locations" +
"\t" + // headers
"\t" + // content hash
"\tEG1-HMAC-SHA256 " +
"client_token=ClientToken;" +
"access_token=AccessToken;" +
"timestamp=20140402T18:05:06+0000;" +
"nonce=185f94eb-537c-4c01-b8cc-2fa5a06aee7f;"
assert.EqualValues(t, expected, data.dataToSign)
}
func TestDataToSignWithHeaders(t *testing.T) {
req, err := http.NewRequest(
http.MethodGet,
"http://akaa-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx.luna-dev.akamaiapis.net/sample-api/v1/property/?fields=x&format=json&cpcode=1234",
http.NoBody)
assert.NoError(t, err)
req.Header.Set("x-a", "va")
req.Header.Set("x-c", "\" xc \"")
req.Header.Set("x-b", " w b")
auth := NewEdgeGridAuth(
"ClientToken", "ClientSecret", "AccessToken",
"x-c", "x-b", "x-a")
auth.now = func() time.Time {
return time.Unix(1376917283, 0) // 20130819T13:01:23Z
}
auth.createNonce = func() (string, error) {
return "ac392096-8aa1-44fd-8c3b-f797d35a6736", nil
}
data, err := auth.signingData(req)
assert.NoError(t, err)
expected := "GET" +
"\thttp" +
"\takaa-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx.luna-dev.akamaiapis.net" +
"\t/sample-api/v1/property/?fields=x&format=json&cpcode=1234" +
"\tx-a:va\tx-b:w b\tx-c:\" xc \"\t" +
"\t" + // content hash
"\tEG1-HMAC-SHA256 " +
"client_token=ClientToken;" +
"access_token=AccessToken;" +
"timestamp=20130819T13:01:23+0000;" +
"nonce=ac392096-8aa1-44fd-8c3b-f797d35a6736;"
assert.EqualValues(t, expected, data.dataToSign)
}