google-cloud-go: KMS cannot Get, Set, or Test IAM
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:
rpc error: code = NotFound desc = The request concerns location 'us-central1' but was sent to location 'global'. Either Cloud KMS is not available in 'us-central1' or the request was misrouted. Make sure the 'x-goog-request-params' metadata is set correctly; see https://cloud.google.com/kms/docs/grpc for more information.
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”.
package main
import (
"context"
"log"
cloudkms "cloud.google.com/go/kms/apiv1"
kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
)
const keyRing = "projects/my-project/..." // Fill with existing keyring
func main() {
ctx := context.Background()
client, err := cloudkms.NewKeyManagementClient(ctx)
if err != nil {
panic(err)
}
resp, err := client.KeyRingIAM(&kmspb.KeyRing{
Name: keyRing,
}).Policy(context.Background())
if err != nil {
panic(err)
}
log.Printf("#%v", resp)
}
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:
func (c *KeyManagementClient) GetKeyRingIAM(ctx context.Context, req *kmspb.GetKeyRingRequest, opts ...gax.CallOption) (*iam.Policy, error) {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", req.GetName()))
ctx = insertMetadata(ctx, c.xGoogMetadata, md)
// Works with or without the additional options here, kept for parity with other KMS client funcs
opts = append(c.CallOptions.GetKeyRing[0:len(c.CallOptions.GetKeyRing):len(c.CallOptions.GetKeyRing)], opts...)
var proto *pb.Policy
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
var err error
// Create the client here so we can pass it the proper context
iamClient := pb.NewIAMPolicyClient(c.Connection())
proto, err = iamClient.GetIamPolicy(ctx, &pb.GetIamPolicyRequest{Resource: req.GetName()}, settings.GRPC...)
return err
}, opts...)
if err != nil {
return nil, err
}
return &iam.Policy{InternalProto: proto}, nil
}
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 an iam.Handle
to the user directly.
Alternatives
- Provide top-level functionality in the IAM package to add headers
- Allow objects to pass a context to the IAM handler and store it on the struct (gross)
- Force uses to manually add metadata fields (even grosser)
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 44 (30 by maintainers)
Got it, thanks for the pointer. I was able to modify the context and got this to work:
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.
“global” is just-another-named-region in Cloud KMS, with the property that it is served out of multiple globally redundant data centers.
Even for “global”, GFEs need to know to address the request to the “global” stubby backend.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?
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.