Open jacobo opened 3 years ago
looks like there is a related open feature request at https://github.com/googleapis/google-auth-library-ruby/issues/270
Is there some simple call I could make to this gem to determine what context I'm in?
@jacobo Have you tried using ruby-cloud-env? (It's already a dependency of google-cloud-storage
via google-cloud-core
.)
For example:
require "google/cloud/env"
env = Google::Cloud.env
env.kubernetes_engine?
Background: The signer
parameter and examples showing sign_service_account_blob
usage were added in PR #7091.
Current state of FR(1/16/23): In #7091, a new signer parameter was introduced to allow passing in a method to create signature from a payload to support signed URLs. However, it still requires boiler plate code to check the environment a client is in (GCE or local dev env) to determine how to call #signed_url
when generating a Signed URL.
Google::Cloud.env#lookup_metadata
IAMCredentials#sign_service_account_blob
when in GCE environment Google::Cloud.env#compute_engine?
These steps should reduce overhead of boiler plate code using signer parameter in signed_url
and at the same existing signer / issuer support provides an escape hatch for more complex situations that can't be auto-detected.
require "google/cloud/env" # Dependency of google-cloud-storage gem
require "google/cloud/storage"
require "google/apis/iamcredentials_v1"
require "googleauth"
storage = Google::Cloud::Storage.new
bucket = storage.bucket "my-todo-app"
file = bucket.file "avatars/heidi/400x400.png", skip_lookup: true
env = Google::Cloud.env
if env.compute_engine?
# Issuer is the service account email that the Signed URL will be signed with
# and any permission granted in the Signed URL must be granted to the
# Google Service Account.
issuer = env.lookup_metadata "instance", "service-accounts/default/email"
# Create a lambda that accepts the string_to_sign
signer = lambda do |string_to_sign|
IAMCredentials = Google::Apis::IamcredentialsV1
iam_client = IAMCredentials::IAMCredentialsService.new
# Get the environment configured authorization
scopes = ["https://www.googleapis.com/auth/iam"]
iam_client.authorization = Google::Auth.get_application_default scopes
request = Google::Apis::IamcredentialsV1::SignBlobRequest.new(
payload: string_to_sign
)
resource = "projects/-/serviceAccounts/#{issuer}"
response = iam_client.sign_service_account_blob resource, request
response.signed_blob
end
url = file.signed_url method: "GET", issuer: issuer,
signer: signer
else
url = file.signed_url method: "GET"
end
References:
Hi, I am open to writing a PR to make code changes for this feature but wanted to start with an issue to get feedback.
When developing locally I can generate signed URL simply with:
However, when running my code from within an eks pod with access grant via workload identitiy (https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity)
I get the error:
Based on https://github.com/salrashid123/gcpsamples/blob/master/gcs_keyless_signedurl/main.py I was able to write some equivalent ruby code that works for generating a working signed URL (not the most well-factored code, just a proof-of-concept)
So... I was thinking perhaps we could/should just change the implementation of
signed_url
to detect the conditions under which key-based signing will fail and automatically attempt this meta data service token + signBlob service method.Or perhaps it's already exposed somewhere else within the depths of this ruby gem? (in which case in the very least the error message you see could be updated to point that out)
Thanks, Jacob
Update based on https://googleapis.dev/ruby/google-cloud-storage/latest/Google/Cloud/Storage/File.html#signed_url-instance_method I have a better version
And the only things that are "custom" to my use case in this example end up being arguments into the
signed_url
method. so perhaps a simplification this gem could make would be to auto-generate thesigner
procFinally, I need a way to tell which version of the
signed_url
method args to use.When running locally I have a credentials JSON file. so I need only call
remote_file.signed_url method: "PUT", content_type: "application/json"
Whereas from my pod I have to callremote_file.signed_url method: "PUT", content_type: "application/json", issuer: issuer, signer: signer
If I do it backwards (specify an issue and signer when not on a pod) I get
Is there some simple call I could make to this gem to determine what context I'm in?