googleapis / google-cloud-ruby

Google Cloud Client Library for Ruby
https://googleapis.github.io/google-cloud-ruby/
Apache License 2.0
1.35k stars 538 forks source link

Authentication question #13121

Closed danielalmond closed 2 years ago

danielalmond commented 3 years ago

I have a service account I am impersonating and have generated a token using the method described here: https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials#sa-credentials-oauth

curl -X POST \ -H "Authorization: Bearer "$(gcloud auth application-default print-access-token) \ -H "Content-Type: application/json; charset=utf-8" \ -d @request.json \ https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/SA_NAME@PROJECT_ID.iam.gserviceaccount.com:generateAccessToken

Is there any way to use the token that is generated as credentials for the ruby gcp sdk?

quartzmo commented 3 years ago

@danielalmond Thanks for this great question. I'll try to look into it today.

quartzmo commented 3 years ago

I believe you should be able to use the OAuth 2.0 access token to construct a Signet::OAuth2::Client instance that can then be set as the credentials inside a config initialization block when creating a client. I haven't been able to find a published example showing how to do it, however. Which clients (GCP products) are you intending to authenticate in this way?

salrashid123 commented 3 years ago

disclaimer: i don't really know ruby but i've worked on the other gcp auth libraries for impersonation and downscoped

i think the technique described above will work for many client libaries but not others like bigquery since that uses some other type of low level transport/credentials class structure.

many other gcp language auth libraries allows you to define a staic credential that you can inject a value into and use as is (maybe a feature request derived from this issue?)

note, using static, non-refreshable access tokens is useful in a couple of different areas where you happens to have (i.,e given one by a tokenbroker) a token that is impersonated or is downscoped


anyway, here's what i've gotten so far

# gem install  --user-install google-cloud-secret_manager gapic-common
# require "google/cloud/secret_manager"
# client = Google::Cloud::SecretManager.secret_manager_service

require "google/cloud/secret_manager/v1"
require "google/cloud/bigquery"
require 'googleauth'

require 'signet/oauth_2/client'

sclient = Signet::OAuth2::Client.new()
sclient.access_token="ya29.a0ARrd..."

client = ::Google::Cloud::SecretManager::V1::SecretManagerService::Client.new do |config|
    config.credentials = sclient
end

name = "projects/1071284184436/secrets/thepassword/versions/1"

response = client.access_secret_version(
      name:    name,
)

payload = response.payload.data
puts "Plaintext: #{payload}"
quartzmo commented 3 years ago

@salrashid123 Thank you for providing this example! I was about to start work on my own very similar demonstration when your post appeared, and the help is much appreciated. Instead, I will see if I can get Signet::OAuth2::Client to work with google-cloud-bigquery, which as you noted is not gRPC-based and depends on google-api-ruby-client (google-cloud-storage is similar.)

@danielalmond Can you verify if the example above can be adapted for your use case?

danielalmond commented 3 years ago

Definitely, I will try it . Thanks @salrashid123 .

For our use case, we are actually using the ruby-based inspec-gcp to verify a bunch of gcp infra objects from a variety of different sources.

quartzmo commented 3 years ago

@salrashid123 Do you mind editing your post and removing the google-cloud-bigquery section from your example to make it clearer for people who are just looking for the Gapic (gRPC) library solution?

quartzmo commented 3 years ago

@danielalmond Which libraries from this repo are you using via inspec-gcp?

salrashid123 commented 3 years ago

done, also for ref, in nodejs, ther's a similar problem where you can inject a "client" into the grpc based services but not tohers like GCS (and presumably Bigquery too).

https://github.com/googleapis/google-auth-library-nodejs/issues/1210

quartzmo commented 3 years ago

@salrashid123 Can you try this?

token = "ya29.a0ARrd..."

require "google/cloud/bigquery"

client = Signet::OAuth2::Client.new
client.access_token = token
Google::Cloud::Bigquery.configure do |config|
  config.credentials = client
end

bigquery = Google::Cloud::Bigquery.new
quartzmo commented 3 years ago

@salrashid123 Never mind, I tried it and get this error:

irb(main):006:1* Google::Cloud::Bigquery.configure do |config|
irb(main):007:1*   config.credentials = client
irb(main):008:0> end
Invalid value #<Signet::OAuth2::Client:0x00007fcd7213ee98 @authorization_uri=nil, @token_credential_uri=nil, @client_id=nil, @client_secret=nil, @code=nil, @expires_at=nil, @issued_at=nil, @issuer=nil, @password=nil, @principal=nil, @redirect_uri=nil, @scope=nil, @target_audience=nil, @state=nil, @username=nil, @access_type=:offline, @expiry=60, @extension_parameters={}, @additional_parameters={}, @access_token="ya29.c.KskCC..."> for key :credentials. Setting anyway. at (irb):7:in `block in <main>'
(Object doesn't support #inspect)
=> 
irb(main):009:0> 
irb(main):010:0> bigquery = Google::Cloud::Bigquery.new
/Users/quartzmo/.rbenv/versions/3.0.1/gemsets/quartzmo/gems/googleauth-0.16.2/lib/googleauth/signet.rb:118:in `rescue in retry_with_error': Unexpected error: #<ArgumentError: Missing token endpoint URI.> (Signet::AuthorizationError)
    from /Users/quartzmo/.rbenv/versions/3.0.1/gemsets/quartzmo/gems/googleauth-0.16.2/lib/googleauth/signet.rb:107:in `retry_with_error'
    from /Users/quartzmo/.rbenv/versions/3.0.1/gemsets/quartzmo/gems/googleauth-0.16.2/lib/googleauth/signet.rb:80:in `fetch_access_token!'
    from /Users/quartzmo/.rbenv/versions/3.0.1/gemsets/quartzmo/gems/googleauth-0.16.2/lib/googleauth/credentials.rb:382:in `initialize'
    from /Users/quartzmo/.rbenv/versions/3.0.1/gemsets/quartzmo/gems/google-cloud-bigquery-1.29.0/lib/google/cloud/bigquery.rb:78:in `new'
    from /Users/quartzmo/.rbenv/versions/3.0.1/gemsets/quartzmo/gems/google-cloud-bigquery-1.29.0/lib/google/cloud/bigquery.rb:78:in `new'
quartzmo commented 2 years ago

@danielalmond Are you using inspec-gcp/libraries/gcp_backend.rb? It looks to me like it only supports service accounts and default credentials.

quartzmo commented 2 years ago

In the BigQuery scenario shown above the short-lived token is already set in Signet::OAuth2::Client#access_token before the client is passed to Google::Auth::Credentials#initialize, which errors on the call to Signet::OAuth2::Client#fetch_access_token!. (Signet::OAuth2::Client#fetch_access_token! depends on a token endpoint URI and various other inputs, none of which are needed if you already have the short-lived access token.)

quartzmo commented 2 years ago

This issue for REST-based libraries was fixed in googleauth v1.1.0. The following example can now be used to impersonate service accounts using a short-lived token in the libraries in this repo that depend on google-api-ruby-client, such as google-cloud-bigquery and google-cloud-storage. (Short-lived tokens have always worked with gRPC-based libraries, as shown above.)

token = "ya29.a0ARrd..."

require "google/cloud/bigquery"

client = Signet::OAuth2::Client.new
client.access_token = token
Google::Cloud::Bigquery.configure do |config|
  config.credentials = client
end

bigquery = Google::Cloud::Bigquery.new
datasets = bigquery.datasets