-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
KMS cannot Get, Set, or Test IAM #1285
Comments
@jskeet is this something you've observed in C#? |
Only 5 hits for |
I'm going to chime in that I don't like the way Specifically, I don't like that there is something to manually maintain, if we choose to expose IAM permissions on additional resource types in the future. |
@broady the IAM methods are handwritten |
@bdhess ah, right. other APIs do not seem to require x-goog-request-params for IAM methods, is KMS special in this respect? I'm guessing it's fine to add to all requests (i.e., to all APIs), can you confirm?
I don't fully understand, can you rephrase? Do you mean that you think IAM functionality should not be hand-written, but auto-generated? I agree -- I was surprised that the hooks to IAM are manually written. |
There are some other APIs that require the metadata. It's because KMS is regionalized, and GFEs need to forward the request to the correct backend Stubby service. I'm not saying this shouldn't be handwritten (although the GAPIC generator can generate these for all of the other silver languages). But that I'd prefer a single generic method that just takes a resource path string to multiple strongly-typed methods that take specific kinds of resources. |
cc @pongad |
I'll have to have a look tomorrow |
@jskeet May be a false alarm - seems like it's Go specific. Sorry! |
pinged @bdhess offline to clarify, here's my interpretation on his comments about the IAM methods in the KMS package:
Since only the name is used from the CryptoKey struct, anyway. (See Seth's code above as an example of seemingly unnecessary struct creation.) Anyway, this might make sense for KMS but not for other APIs, which might not pass around or ask the developer to construct full resource paths as often as KMS does. Worth more investigation and thought (discussion on another thread, probably). Though @bdhess points out that since it's totally busted already, it might be fine to change at the same time as making a bug fix. But it seems like we should just add x-goog-request-params to all IAM requests in the cloud.google.com/go/iam package. |
I'd go further and say
Should both converge to
Not wedded to the names, but I'd like to get down to just one signature. We're planning to add at least one new resource type this quarter that will have IAM permissions, it would be great to have no changes required to handwritten Go client code in order to support. |
We foolishly set the gapic to GA in 205084741 before we were aware that the library needed to go through a separate GA process than the service... sigh. :( We could probably make a breaking change since - if I'm reading this bug right - I doubt this works for anyone? |
This will not work for anyone today unless they went through the trouble to add the correct metadata pairs to the context before making the IAM call. It's not impossible, but seems very unlikely. |
Is there such a thing as a global KMS keyring, and if so, would this functionality work? |
"global" is just-another-named-region in Cloud KMS, with the property that it is served out of multiple globally redundant data centers. |
That might be challenging because you don't know what that KV pair is. It varies by resource (sometimes it's "name", "parent", or "resource").
It works iff keys are in the "global" region (i.e. a key projects/my-project/locations/global/keyRings/my-keyring).
I don't actually think it's possible today since there's no way to inject metadata into the iam.Handle. The user would have to fully re-construct the grpc call with gax. While that's not impossible, it definitely borders on terrible.
In my testing, global does appear to work as-is with this library |
They're well defined for the three IAM methods, though.
Confirmed-- there is a default routing in GFEs that I didn't know existed. GFEs will route to our 'global' region in the absence of metadata (and they will also route to the 'global' region if 'global' is provided in the metadata). Withdrawing my request to make a breaking change (though I would like to still consider adding the generic method).
It doesn't need to be in the iam.Handle; you could add it to the calls made with the handle, e.g.
Indeed; I stand corrected. |
Got it, thanks for the pointer. I was able to modify the context and got this to work: package main
import (
"context"
"fmt"
cloudkms "cloud.google.com/go/kms/apiv1"
kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
"google.golang.org/grpc/metadata"
)
const keyRing = "projects/my-project/..." // Fill with existing keyring
func main() {
ctx := context.Background()
client, err := cloudkms.NewKeyManagementClient(ctx)
if err != nil {
panic(err)
}
ctx = metadata.AppendToOutgoingContext(context.Background(),
"x-goog-request-params", fmt.Sprintf("name=%v", keyRing))
policy, err := client.KeyRingIAM(&kmspb.KeyRing{Name: keyRing}).Policy(ctx)
if err != nil {
panic(err)
}
fmt.Printf("Retrieved policy: %#v\n", policy)
} That's actually not horrible, but it requires some pretty specific knowledge that I'd expect the client library to take care of for me. |
A few things, maybe @pongad can help... (1) the IAM gapic isn't generated (though the "admin" and "credentials" packages are) even though it seems to be specified under "language_settings" in the gapic config. Is there some other manual step I don't know about? Is there a reason we don't have this generated? Is that our policy for packages that have hand-written clients? (Makes sense. No gapic for bigtable, datastore, etc. but there is for spanner.) (2) should we then add the (3) should we then have cloud.google.com/go/iam depend on the gapic? If no to (3) then let's just add it manually to the existing hand-written iam package. |
I'll also add, though it's somewhat tangential to this issue, that Cloud KMS exposes the google.cloud.locations.Locations service, and that is not currently exposed in the Go client. |
@bdhess where? I don't see it in the KMS proto service definition, nor do I see it in the Ruby client. Please open another bug if this is not WAI or ping me on chat. And yes, let's keep this bug specifically to adding x-goog-request-params to the iam package. |
Yeah, I'm wrong, it's not in the GAPIC config at all, so doesn't appear in any of the GAPIC clients. We need to add that, but totally out of scope for this issue. (It's not in the KMS protos though; like IAM, it has its own protos) |
I have moved teams, so I'll add @noahdietz who now owns the Go generator. To answer @broady's question.
|
I think the fact that anyone that chooses to generate IAM will not be bitten by this bug is a good enough reason to add header_request_params to the gapic yaml (and to use it). @jadekler wdyt? can do separately, since the fix for this particular bug is likely a 3 LOC delta change. |
Hey @jadekler @noahdietz - have we decided on the best path forward here? I'm happy to help out, but don't want to spend cycles going in the wrong direction 😄 |
Hi everyone, I'm taking a look at the proposed solution, will get back to y'all soon |
Hi all, sorry for the delay. Expanding on this plan it looks like we need to: Ok, tell me what you think about this plan:
This works because the gapic client will by default use req.Resource to set x-goog-request-params, unlike the hand-written iam/iam.go methods. Does this look good to everyone? edit: btw @broady @pongad @noahdietz I'm not sure how |
For the third point, I think I'd rather accept an interface than a string. While right now all KMS resources are uniquely identifiable as a string, the future is unknown. Also, if other libs move to this style, they might need a method that defines the unique resource URL. type IAMResourcePather interface {
IAMResourceName() string
}
func (c *KeyManagementClient) ResourceIAM(i IAMResourcePather) *IAMHandle I'm not tied to the names, but the interface might give us more flexibility in the future. |
The Say we define func (c *IamPolicyClient) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "resource", req.GetResource()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md) Per the earlier example, it seems like this is the desired behavior, to have the resource name to be provided in the ctx = metadata.AppendToOutgoingContext(context.Background(),
"x-goog-request-params", fmt.Sprintf("name=%v", keyRing))
policy, err := client.KeyRingIAM(&kmspb.KeyRing{Name: keyRing}).Policy(ctx) |
@sethvargo I'm 👎 on interfaces: they're harder for users to reason about, much harder for us to document, and harder for us to code around the edge cases. Do we anticipate anything other than string? If so, then I'd rather go back to having per-resource methods with well defined inputs. @noahdietz Gotcha! Thanks. Then IIUC the amended plan would be:
Does that look right? |
Yes, and this entails changes to the google/iam/v1/iam_gapic.yaml. Also worth noting, Pub/Sub depends on the manual IAM client in a similar way so any changes we make there should be tested with Pub/Sub as well. |
As I reread this, I'm a little confused on what the If the value of KMS The GAPIC |
|
But here's prior art for GAPIC-generated Java code: |
Thanks everyone for testing/researching this. I've parked the changes we've discussed here in a CL. This will require changes to the relevant GAPIC configurations in IAM (reference PR) which I am figuring out if we can do or if the service owner must do. |
Hey all so I talked with @lukesneeringer about how this should be done and we weren't sure this was the right approach. Luke, can you weigh in with what you know about the IAM methods. Hopefully I represented things fairly. |
As best as I can tell, the issue here seems to be that KMS is different than the rest of Google (except Pub/Sub, a known separate issue) in how it exposes IAM methods. The general way this is done is for the three IAM methods to be delcared within an API's own services. A canonical example is here, but there are many others. If KMS followed the standard here, we would get the header params from the HTTP binding, as described in {ping me for internal link if you need it}. I would rather not add these new components to what we generate (and then add a new sort-of hack on top of them) in order to support a non-standard case. My feeling is that either KMS should follow the standard pattern (likely no longer possible since it is in GA) or supporting this should be handled manually. |
Do we yet know the impact of this on other platforms? I haven't looked at every aspect of KMS myself, but I'm aware of a large set of samples being prepared - should I check whether that includes IAM calls? |
I'll leave this to @bdhess to reply to except to mention that I'm OK making a backwards-incompatible change * since approximately zero users use these methods, and they are roughly broken in their current state by not setting x-goog-request-params. * If we went this route, I would suggest deprecation + new methods in one release, removal of old method in another release. |
To Luke's point, here is another service that has If this question & its implied solution is irrelevant b.c of release stuff, please say so 😄 Thanks |
@noahdietz and I caught up offline; I think he will follow up with @lukesneeringer @jskeet AFAIK this issue is limited to Golang; it seems the Golang GAPIC generator lacks support for |
I spoke with Luke offline and we are good to go here. To restate the plan, we want to:
Furthermore, I am taking an item to investigate why the |
After digging into the necessary changes to generate an IAMPolicy GAPIC, I'm finding that this is not possible. I spoke too soon. I think we need to make this Why? Because the IAMPolicy service does not have a default host. @jadekler @bdhess Can we do this request param addition in one of the manual layers? Sorry to get hopes up about generating a GAPIC, I am learning as I go and I appreciate your patience. |
We went with the manual layer approach & added the new |
Client
KMS
Describe Your Environment
MacBook Pro (15-inch, 2017), 10.14.2 (18C54)
2.8 GHz Intel Core i7
16 GB 2133 MHz LPDDR3
Radeon Pro 555 2048 MB
Intel HD Graphics 630 1536 MB
But also reproducible everywhere
Expected Behavior
KMS clients should be able to Get, Set, and Test IAM policies.
Actual Behavior
Performing any Get, Set, or Test operations using the
iam.Handle
returns the following error:Where "us-central1" corresponds to the region in which my KMS keys exist. I conferred with the KMS team, and it appears that the
x-goog-request-params
is not set on IAM calls, which appears to be the root cause.Reproduction
Run this sample, changing the
keyRing
constant to the full resource URL of a keyring in a project in any location other than "global".Diagnoses
The
KeyRingIAM
func creates a handle wrapper around the KMS client's Connection object which drops straight into the iam package. There doesn't appear to be a way to pass additional metadata pairs to the IAM client, so when gax invokes the policy requests, they fail.I confirmed the API works as-expected with the headers on the request by adding the following method to the KMS client itself so I can access the request fields:
Note this function lives in the kms package and injects the
x-google-request-params
into the context before creating the IAM client. I think this might be the best approach to preserve those headers without significant altercations to the iam package itself, but it means that the client does not given aniam.Handle
to the user directly.Alternatives
The text was updated successfully, but these errors were encountered: