Skip to content

Commit f8ca6ce

Browse files
committed
storage: fix incorrect API scopes for IAM SignBlob API
Previously when a service account attempted to use the IAM SignBlob API, the request would fail with a 403 `ACCESS_TOKEN_SCOPE_INSUFFICIENT` because the wrong scope was requested. As documented in https://cloud.google.com/iam/docs/reference/credentials/rest/v1/projects.serviceAccounts/signBlob, either `https://www.googleapis.com/auth/iam` or `https://www.googleapis.com/auth/cloud-platform` is needed. This commit fixes an issue where the default authorization header with the `https://www.googleapis.com/auth/devstorage.full_control` scope was being used by the IAM service. This occurred because the previous code did not actually set the scope properly, and for the IAM service to work properly, we need to request a new access token with the correct scope. Note that the service account in question needs to have the `Service Account Token Creator` IAM role to work. Closes #599
1 parent afd289d commit f8ca6ce

File tree

3 files changed

+24
-15
lines changed

3 files changed

+24
-15
lines changed

lib/fog/google/shared.rb

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,21 @@ def initialize_google_client(options)
6767
::Google::Apis.logger.level = ::Logger::DEBUG
6868
end
6969

70-
auth = nil
70+
initialize_auth(options).tap do |auth|
71+
::Google::Apis::RequestOptions.default.authorization = auth
72+
end
73+
end
7174

75+
def initialize_auth(options)
7276
if options[:google_json_key_location] || options[:google_json_key_string]
73-
auth = process_key_auth(options)
77+
process_key_auth(options)
7478
elsif options[:google_auth]
75-
auth = options[:google_auth]
79+
options[:google_auth]
7680
elsif options[:google_application_default]
77-
auth = process_application_default_auth(options)
81+
process_application_default_auth(options)
7882
else
79-
auth = process_fallback_auth(options)
83+
process_fallback_auth(options)
8084
end
81-
82-
::Google::Apis::RequestOptions.default.authorization = auth
83-
auth
8485
end
8586

8687
##

lib/fog/storage/google_json.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class GoogleJSON < Fog::Service
2929

3030
# Version of IAM API used for blob signing, see Fog::Storage::GoogleJSON::Real#iam_signer
3131
GOOGLE_STORAGE_JSON_IAM_API_VERSION = "v1".freeze
32-
GOOGLE_STORAGE_JSON_IAM_API_SCOPE_URLS = %w(https://www.googleapis.com/auth/devstorage.full_control).freeze
32+
GOOGLE_STORAGE_JSON_IAM_API_SCOPE_URLS = %w(https://www.googleapis.com/auth/iam).freeze
3333

3434
# TODO: Come up with a way to only request a subset of permissions.
3535
# https://cloud.google.com/storage/docs/json_api/v1/how-tos/authorizing

lib/fog/storage/google_json/real.rb

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,12 @@ class Real
1010

1111
def initialize(options = {})
1212
shared_initialize(options[:google_project], GOOGLE_STORAGE_JSON_API_VERSION, GOOGLE_STORAGE_JSON_BASE_URL)
13+
@options = options.dup
1314
options[:google_api_scope_url] = GOOGLE_STORAGE_JSON_API_SCOPE_URLS.join(" ")
1415
@host = options[:host] || "storage.googleapis.com"
1516

1617
# TODO(temikus): Do we even need this client?
1718
@client = initialize_google_client(options)
18-
# IAM client used for SignBlob API
19-
@iam_service = ::Google::Apis::IamcredentialsV1::IAMCredentialsService.new
20-
apply_client_options(@iam_service, {
21-
google_api_scope_url: GOOGLE_STORAGE_JSON_IAM_API_SCOPE_URLS.join(" ")
22-
})
2319

2420
@storage_json = ::Google::Apis::StorageV1::StorageService.new
2521
apply_client_options(@storage_json, options)
@@ -141,6 +137,18 @@ def default_signer(string_to_sign)
141137
return key.sign(digest, string_to_sign)
142138
end
143139

140+
# IAM client used for SignBlob API.
141+
# Lazily initialize this since it requires another authorization request.
142+
def iam_service
143+
return @iam_service if defined?(@iam_service)
144+
145+
@iam_service = ::Google::Apis::IamcredentialsV1::IAMCredentialsService.new
146+
apply_client_options(@iam_service, @options)
147+
iam_options = @options.merge(google_api_scope_url: GOOGLE_STORAGE_JSON_IAM_API_SCOPE_URLS.join(" "))
148+
@iam_service.authorization = initialize_auth(iam_options)
149+
@iam_service
150+
end
151+
144152
##
145153
# Fallback URL signer using the IAM SignServiceAccountBlob API, see
146154
# Google::Apis::IamcredentialsV1::IAMCredentialsService#sign_service_account_blob
@@ -162,7 +170,7 @@ def iam_signer(string_to_sign)
162170
)
163171

164172
resource = "projects/-/serviceAccounts/#{google_access_id}"
165-
response = @iam_service.sign_service_account_blob(resource, request)
173+
response = iam_service.sign_service_account_blob(resource, request)
166174

167175
return response.signed_blob
168176
end

0 commit comments

Comments
 (0)