fix(#5563): sets namespace using vault client methods instead of using raw request object#5589
fix(#5563): sets namespace using vault client methods instead of using raw request object#5589zscholl wants to merge 8 commits intocert-manager:masterfrom zscholl:zscholl/fix-vault-namespace
Conversation
|
Hi @zscholl. Thanks for your PR. I'm waiting for a cert-manager member to verify that this patch is reasonable to test. If it is, they should reply with Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: zscholl The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
/kind bug |
|
/cc inteon |
Signed-off-by: Zak Scholl <zak.scholl@shopify.com>
|
@zscholl I made some changes to your PR to make sure the solution still works when clients are shared across threads. |
Signed-off-by: Zak Scholl <zak.scholl@shopify.com>
|
/retest-required |
1 similar comment
|
/retest-required |
|
@inteon your approach looks much better 😄. Thanks for committing that. 🙏 I threw in some tests that heavily borrow from Vault's tests for the WIthNamespace function. But these should sufficiently validate and catch any regression as they check the headers that are received on the server side to ensure that the namespace is included when it should be. |
internal/vault/vault_test.go
Outdated
| issuer *cmapi.VaultIssuer | ||
| } | ||
|
|
||
| func testHTTPServer(t *testing.T, handler http.Handler) (*vault.Config, net.Listener) { |
There was a problem hiding this comment.
Can we simplify this code by replacing this function with https://pkg.go.dev/net/http/httptest#NewServer?
There was a problem hiding this comment.
indeed we can! Great suggestion 👍
There was a problem hiding this comment.
and you can also just call Close() on server instead of server.Listener.
There was a problem hiding this comment.
sorry for all the nitpicking, but I would also use server.URL instead of server.Listener.Addr()
There was a problem hiding this comment.
no worries at all! These are all valid and good suggestions. I appreciate your keen eye :)
Signed-off-by: Zak Scholl <zak.scholl@shopify.com>
Signed-off-by: Zak Scholl <zak.scholl@shopify.com>
Signed-off-by: Zak Scholl <zak.scholl@shopify.com>
Signed-off-by: Zak Scholl <zak.scholl@shopify.com>
Signed-off-by: Zak Scholl <zak.scholl@shopify.com>
wallrj
left a comment
There was a problem hiding this comment.
I looked at the WithNamespace method in the vault SDK and wondered if the fix could be as simple as:
master...wallrj:fix-vault-namespace-rjw
It's not clear to me why the namespace should only be applied to RawRequest?
If that is necessary, please add some explanatory comments to the code for the benefit of future maintainers.
Hey @wallrj! That's a good insight. The namespace doesn't necessarily have to be applied to the RawRequest, but it does have to be applied to the client. However the issue with the approach you suggested is that all requests made with the client cannot have the namespace set. Particularly the I tested this approach early on and Vault enterprise returns a 404 when sending the namespace header to a root only API path. e.g.: Alternatively we could use the approach you suggested and then explicitly use WithNamespace again in the I'll add a comment to the Thank you for the feedback 🙏 |
Signed-off-by: Zak Scholl <zak.scholl@shopify.com>
|
@zscholl: The following tests failed, say
Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here. |
|
Thanks @zscholl , A few more thoughts, and forgive me for dragging this out. In hashicorp/vault#14934 (comment) @ncabatoff wrote:
If that is true, then I'd expect the existing health check in the cert-manager Vault Issuer to fail for users of HCP Vault and where their enterprise vault has not been configured to allow access to the root namespace. I'm dwelling on HCP Vault because that is what @schmitz-chris reported using in the original issue: Another confusion for me is that that original issue reports that the namespace feature is not working since cert-manager 1.9, but the new Vault client (which changed the namespace behaviour) was introduced by @inteon in 39fa9f5 which was released in 1.10. Refs: |
|
Actually, perhaps In hashicorp/vault#209 (comment) @mitchellh wrote:
And in the documentation for So that is consistent with the statement that Most endpoints under /v1/sys that require authentication are not available. |
|
@wallrj I don't know for sure about the HCP behavior, but your reading of the docs seems sound to me. That
We've deployed cert-manager 1.9.2 and did not observe the namespace bug. That said, we are using self-deployed Enterprise Vault so I cannot say for certain that there isn't a specific problem with 1.9 and HCP. |
| type Client interface { | ||
| NewRequest(method, requestPath string) *vault.Request | ||
| RawRequest(r *vault.Request) (*vault.Response, error) | ||
| NamespacedRawRequest(vaultIssuer *v1.VaultIssuer, r *vault.Request) (*vault.Response, error) | ||
| SetToken(v string) | ||
| Token() string | ||
| Sys() *vault.Sys | ||
| } |
There was a problem hiding this comment.
I dislike this change because:
- before, the
Clientinterface could be described as "the subset of the methods of the Vault SDK client which we use in cert-manager", which would be clear to someone reading the code.
Now it represents a Client which is subtly different than the vault SDK. - it muddles up two APIs which IMO should be separate; the NamespacedRawRequest now accepts a cmapi.VaultIssuer pointer, where it seems to me that it only really needs to accept a new
namespace stringparameter.
| Token() string | ||
| Sys() *vault.Sys |
There was a problem hiding this comment.
Both Token and Sys are unused in the implementation and should be removed....probably in a separate PR because we need to keep this PR as small as possible so that it can be backported.
| v.addVaultNamespaceToRequest(request) | ||
|
|
||
| resp, err := client.RawRequest(request) | ||
| resp, err := client.NamespacedRawRequest(v.issuer.GetSpec().Vault, request) |
There was a problem hiding this comment.
| resp, err := client.NamespacedRawRequest(v.issuer.GetSpec().Vault, request) | |
| resp, err := client.NamespacedRawRequest(v.issuer.GetSpec().Vault.Namespace, request) |
...would be much more readable, I think.
| healthURL := path.Join("/v1", "sys", "health") | ||
| healthRequest := v.client.NewRequest("GET", healthURL) | ||
| healthResp, err := v.client.RawRequest(healthRequest) | ||
| healthResp, err := v.client.NamespacedRawRequest(nil, healthRequest) |
There was a problem hiding this comment.
| healthResp, err := v.client.NamespacedRawRequest(nil, healthRequest) | |
| healthResp, err := v.client.NamespacedRawRequest("", healthRequest) |
....would make it clearer that this is a non-namespaced request.
| } | ||
| }) | ||
| } | ||
| } |
There was a problem hiding this comment.
I'm not convinced we should be testing for the presence of the X-Vault-Namespace header.
That's an implementation detail of the Vault server.
What if Hashicorp introduce a slightly different Header or some other mechanism for configuring the namespace?
The vault SDK client has a Namespace() method which tells us which namespace it's going to use, and in
#5591 I explored a slightly different way of testing this and implemented the namespace and non-namespace client following a pattern I found in Hashicorp Nomad.
Please tell me what you think of that approach and if you both prefer this PR, I'll concede and approve this PR.
There was a problem hiding this comment.
I prefer your approach in #5591.
I think based on the points you raised, #5591 is clearer and more consistent. It also eliminates ambiguity around when to use or not use a namespaced request. In your implementation you just use the default client for everything except sys calls.
I also agree with your point about this test being tightly coupled to Vault's implementation. And it's better to just check that the client has the expected namespace set. I did like being able to check what was actually received on the server side, but that's a better test for the vault client itself.
I'm all for closing this PR in favor of #5591 🚀
Pull Request Motivation
This change fixes #5563 by changing the vault client logic to instead use the vault client's functions to set the namespace, rather than passing in a new request object with custom headers.
Why is this necessary?
The behavior of the vault client changed in vault api version
v1.11.0with this commit. The impact of this change means that any headers that are set in the request object passed in toRawRequestare wiped out if there are no headers set in the vault client object.Before vault API
v1.11.0RawRequestrespected headers that were passed in via the request argument.The vault API dependency was upgraded in cert-manager
v1.10.0: 39fa9f5 and so anyone using this version or later will see that the namespace setting does nothing and all requests to vault go to the default root namespace.Kind
bug
Release Note