Comment générer une URL signée Blob dans Google Cloud Run?
Sous Google Cloud Run, vous pouvez sélectionner le compte de service exécuté par votre conteneur. L'utilisation du compte de service de calcul par défaut ne parvient pas à générer une URL signée.
Le travail autour répertorié ici fonctionne sur Google Cloud Compute - si vous autorisez toutes les étendues pour le compte de service. Il ne semble pas être loin de faire cela dans Cloud Run (pas que je puisse trouver).
https://github.com/googleapis/google-auth-library-python/issues/50
Choses que j'ai essayées:
- Attribuer au compte de service le rôle:
roles/iam.serviceAccountTokenCreator - Vérification de la solution de contournement dans le même projet GCP dans une machine virtuelle (vs Cloud Run)
- Vérifié que le code fonctionne localement dans le conteneur avec le compte de service chargé à partir de la clé privée (via le fichier json).
from google.cloud import storage
client = storage.Client()
bucket = client.get_bucket('EXAMPLE_BUCKET')
blob = bucket.get_blob('libraries/image_1.png')
expires = datetime.now() + timedelta(seconds=86400)
blob.generate_signed_url(expiration=expires)
Échoue avec:
you need a private key to sign credentials.the credentials you are currently using <class 'google.auth.compute_engine.credentials.Credentials'> just contains a token. see https://googleapis.dev/python/google-api-core/latest/auth.html#setting-up-a-service-account for more details.
/usr/local/lib/python3.8/site-packages/google/cloud/storage/_signing.py, line 51, in ensure_signed_credentials
Essayer d'ajouter la solution de contournement,
Error calling the IAM signBytes API:
{ "error": { "code": 400,
"message": "Request contains an invalid argument.",
"status": "INVALID_ARGUMENT" }
}
Exception Location: /usr/local/lib/python3.8/site-packages/google/auth/iam.py, line 81, in _make_signing_request
Code de contournement mentionné dans le problème Github:
from google.cloud import storage
from google.auth.transport import requests
from google.auth import compute_engine
from datetime import datetime, timedelta
def get_signing_creds(credentials):
auth_request = requests.Request()
print(credentials.service_account_email)
signing_credentials = compute_engine.IDTokenCredentials(auth_request, "", service_account_email=credentials.ser
vice_account_email)
return signing_credentials
client = storage.Client()
bucket = client.get_bucket('EXAMPLE_BUCKET')
blob = bucket.get_blob('libraries/image_1.png')
expires = datetime.now() + timedelta(seconds=86400)
signing_creds = get_signing_creds(client._credentials)
url = blob.generate_signed_url(expiration=expires, credentials=signing_creds)
print(url)
Comment générer une URL signée sous Google Cloud Run? À ce stade, il semble que je doive monter la clé de compte de service que je voulais éviter.
EDIT: Pour essayer de clarifier, le compte de service a les autorisations appropriées - il fonctionne dans GCE et localement la clé privée JSON.
Réponses
Oui, vous pouvez, mais j'ai dû plonger profondément pour trouver comment
Si vous allez à la _signing.py file, ligne 623 , vous pouvez voir ceci
if access_token and service_account_email:
signature = _sign_message(string_to_sign, access_token, service_account_email)
...
Si vous fournissez le access_tokenet le service_account_email, vous pouvez utiliser la _sign_messageméthode. Cette méthode utilise l' API SignBlob du service IAM sur cette ligne
C'est important car vous pouvez maintenant signer blob sans avoir localement la clé privée !! Donc, cela résout le problème, et le code suivant fonctionne sur Cloud Run (et j'en suis sûr sur Cloud Function)
def sign_url():
from google.cloud import storage
from datetime import datetime, timedelta
import google.auth
credentials, project_id = google.auth.default()
# Perform a refresh request to get the access token of the current credentials (Else, it's None)
from google.auth.transport import requests
r = requests.Request()
credentials.refresh(r)
client = storage.Client()
bucket = client.get_bucket('EXAMPLE_BUCKET')
blob = bucket.get_blob('libraries/image_1.png')
expires = datetime.now() + timedelta(seconds=86400)
# In case of user credential use, define manually the service account to use (for development purpose only)
service_account_email = "YOUR DEV SERVICE ACCOUNT"
# If you use a service account credential, you can use the embedded email
if hasattr(credentials, "service_account_email"):
service_account_email = credentials.service_account_email
url = blob.generate_signed_url(expiration=expires,service_account_email=service_account_email, access_token=credentials.token)
return url, 200
Faites-moi savoir si ce n'est pas clair
Vous ne pouvez pas signer d'URL avec le compte de service par défaut.
Essayez à nouveau votre code de service avec un compte de service dédié avec les autorisations et voyez si cela résout votre erreur
Références et lectures complémentaires:
- https://stackoverflow.com/a/54272263
- https://cloud.google.com/storage/docs/access-control/signed-urls
- https://github.com/googleapis/google-auth-library-python/issues/238