-
Notifications
You must be signed in to change notification settings - Fork 2.2k
fix(#5563): sets namespace using vault client methods instead of using raw request object #5589
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
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. Instructions 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.
Needs 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indeed we can! Great suggestion 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and you can also just call Close()
on server
instead of server.Listener
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry for all the nitpicking, but I would also use server.URL
instead of server.Listener.Addr()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. Instructions 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dislike this change because:
- before, the
Client
interface 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 string
parameter.
Token() string | ||
Sys() *vault.Sys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
@@ -426,7 +439,7 @@ func extractCertificatesFromVaultCertificateSecret(secret *certutil.Secret) ([]b | |||
func (v *Vault) IsVaultInitializedAndUnsealed() error { | |||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.0
with this commit. The impact of this change means that any headers that are set in the request object passed in toRawRequest
are wiped out if there are no headers set in the vault client object.Before vault API
v1.11.0
RawRequest
respected 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