0

Google Cloud Platform a introduit le proxy Identity Aware pour protéger les instances d'environnement flexible d'App Engine de l'accès public.Comment authentifier les fonctions de google cloud pour accéder aux points de terminaison sécurisés du moteur de l'application

Cependant, il n'est pas entièrement clair si cela peut ou devrait être utilisé par Google Cloud Functions qui accède aux points de terminaison de l'API GAE.

Le documentation (avec des exemples Python et Java) indique un flux de travail d'authentification IAP comprenant 1) la génération d'un jeton JWT, 2) la création d'un jeton d'OpenID, 3) Ensuite, envoyer des requêtes à Google App Engine avec un en-tête Authorization: Bearer TOKEN.

Cela semble assez compliqué pour exécuter des fonctions de cloud si une autorisation doit avoir lieu chaque fois qu'une fonction est appelée.

Existe-t-il un autre moyen pour les fonctions de cloud Google d'accéder à des points de terminaison GAE sécurisés?

Répondre

2

Si vous souhaitez passer des appels de GCF à une application protégée par IAP, vous devez en effet utiliser des jetons d'identification. Il n'y a pas d'exemple dans Nodejs donc j'en ai fait un en utilisant this comme référence (le style peut être faux puisque c'est la première fois que je touche nodejs). Contrairement aux jeux de revendications JWT standard, il ne doit pas contenir de portée et avoir l'attribut target_audience.

/** 
* Make IAP request 
* 
*/ 
exports.CfToIAP = function CfToIAP (req, res) { 
    var crypto = require('crypto'), 
     request = require('request'); 
    var token_URL = "https://www.googleapis.com/oauth2/v4/token"; 
    // service account private key (copied from service_account.json) 
    var key = "-----BEGIN PRIVATE KEY-----\nMIIEvQexsQ1DBNe12345GRwAZM=\n-----END PRIVATE KEY-----\n"; 

    // craft JWT 
    var JWT_header = new Buffer(JSON.stringify({ alg: "RS256", typ: "JWT" })).toString('base64'); 
    // prepare claims set 
    var iss = "[email protected]"; // service account email address (copied from service_account.json) 
    var aud = "https://www.googleapis.com/oauth2/v4/token"; 
    var iat = Math.floor(new Date().getTime()/1000); 
    var exp = iat + 120; // no need for a long linved token since it's not cached 
    var target_audience = "12345.apps.googleusercontent.com"; // this is the IAP client ID that can be obtained by clicking 3 dots -> Edit OAuth Client in IAP configuration page 
    var claims = { 
    iss: iss, 
    aud: aud, 
    iat: iat, 
    exp: exp, 
    target_audience: target_audience 
    }; 
    var JWT_claimset = new Buffer(JSON.stringify(claims)).toString('base64'); 
    // concatenate header and claimset 
    var unsignedJWT = [JWT_header, JWT_claimset].join('.'); 
    // sign JWT 
    var JWT_signature = crypto.createSign('RSA-SHA256').update(unsignedJWT).sign(key, 'base64'); 
    var signedJWT = [unsignedJWT, JWT_signature].join('.'); 
    // get id_token and make IAP request 
    request.post({url:token_URL, form: {grant_type:'urn:ietf:params:oauth:grant-type:jwt-bearer', assertion:signedJWT}}, function(err,res,body){ 
    var data = JSON.parse(body); 
    var bearer = ['Bearer', data.id_token].join(' '); 
    var options = { 
     url: 'https://1234.appspot.com/', // IAP protected GAE app 
     headers: { 
     'User-Agent': 'cf2IAP', 
     'Authorization': bearer 
     } 
    }; 
    request(options, function (err, res, body) { 
     console.log('error:', err); 
    }); 
    }); 
    res.send('done'); 
}; 

/** 
* package.json 
* 
*/ 

{ 
    "name": "IAP-test", 
    "version": "0.0.1", 
    "dependencies": { 
    "request": ">=2.83" 
    } 
} 

Mise à jour: clé de compte de service de bottelage est pas recommandé, donc une meilleure option est d'utiliser le serveur de métadonnées. Pour l'exemple ci-dessous pour travailler API Google Identity and Access Management (IAM) doit être activé et App Engine compte de service par défaut devrait avoir le rôle du compte de service Acteur (éditeur par défaut ne suffit pas):

/** 
* Make request from CF to a GAE app behind IAP: 
* 1) get access token from the metadata server. 
* 2) prepare JWT and use IAM APIs projects.serviceAccounts.signBlob method to avoid bundling service account key. 
* 3) 'exchange' JWT for ID token. 
* 4) make request with ID token. 
* 
*/ 
exports.CfToIAP = function CfToIAP (req, res) { 
    // imports and constants 
    const request = require('request'); 
    const user_agent = '<user_agent_to_identify_your_CF_call>'; 
    const token_URL = "https://www.googleapis.com/oauth2/v4/token"; 
    const project_id = '<project_ID_where_CF_is_deployed>'; 
    const service_account = [project_id, 
          '@appspot.gserviceaccount.com'].join(''); // app default service account for CF project 
    const target_audience = '<IAP_client_ID>'; 
    const IAP_GAE_app = '<IAP_protected_GAE_app_URL>'; 

    // prepare request options and make metadata server access token request 
    var meta_req_opts = { 
    url: ['http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/', 
      service_account, 
      '/token'].join(''), 
    headers: { 
     'User-Agent': user_agent, 
     'Metadata-Flavor': 'Google' 
    } 
    }; 
    request(meta_req_opts, function (err, res, body) { 
    // get access token from response 
    var meta_resp_data = JSON.parse(body); 
    var access_token = meta_resp_data.access_token; 

    // prepare JWT that is {Base64url encoded header}.{Base64url encoded claim set}.{Base64url encoded signature} 
    // https://developers.google.com/identity/protocols/OAuth2ServiceAccount for more info 
    var JWT_header = new Buffer(JSON.stringify({ alg: "RS256", typ: "JWT" })).toString('base64'); 
    var iat = Math.floor(new Date().getTime()/1000); 
    // prepare claims set and base64 encode it 
    var claims = { 
     iss: service_account, 
     aud: token_URL, 
     iat: iat, 
     exp: iat + 60, // no need for a long lived token since it's not cached 
     target_audience: target_audience 
    }; 
    var JWT_claimset = new Buffer(JSON.stringify(claims)).toString('base64'); 

    // concatenate JWT header and claims set and get signature usign IAM APIs projects.serviceAccounts.signBlob method 
    var to_sign = [JWT_header, JWT_claimset].join('.');  
    // sign JWT using IAM APIs projects.serviceAccounts.signBlob method 
    var signature_req_opts = { 
     url: ['https://iam.googleapis.com/v1/projects/', 
      project_id, 
      '/serviceAccounts/', 
      service_account, 
      ':signBlob'].join(''), 
     method: "POST", 
     json: { 
     "bytesToSign": new Buffer(to_sign).toString('base64') 
     }, 
     headers: { 
     'User-Agent': user_agent, 
     'Authorization': ['Bearer', access_token].join(' ') 
     } 
    }; 
    request(signature_req_opts, function (err, res, body) { 
     // get signature from response and form JWT 
     var JWT_signature = body.signature; 
     var JWT = [JWT_header, JWT_claimset, JWT_signature].join('.'); 

     // obtain ID token 
     request.post({url:token_URL, form: {grant_type:'urn:ietf:params:oauth:grant-type:jwt-bearer', assertion:JWT}}, function(err, res, body){ 
     // use ID token to make a request to the IAP protected GAE app 
     var ID_token_resp_data = JSON.parse(body); 
     var ID_token = ID_token_resp_data.id_token; 
     var IAP_req_opts = { 
      url: IAP_GAE_app, 
      headers: { 
      'User-Agent': user_agent, 
      'Authorization': ['Bearer', ID_token].join(' ') 
      } 
     }; 
     request(IAP_req_opts, function (err, res, body) { 
      console.log('error:', err); 
     }); 
     }); 
    }); 
    }); 
    res.send('done'); 
}; 
+0

Savez-vous s'il existe un risque de perte de performances pour l'authentification de chaque demande de fonction de cloud? – shongololo

+0

En tant que couche de protection supplémentaire, il aurait un impact de latence de bout en bout faible, mais l'authentification ne se produit pas au niveau de l'instance, donc il ne devrait pas y avoir d'impact sur le temps CPU. –

+0

En fait, ce n'est pas une bonne approche, car il faut regrouper la clé privée qui n'est pas recommandée. Une meilleure approche consiste à utiliser le serveur de métadonnées. Je mettrai à jour l'échantillon plus tard. –

0

Comme indiqué dans ce doc, vous pouvez authentifier à une API Google Cloud Platform (GCP) en utilisant:

1- Comptes de service (méthode préférée) - utilisation d'un compte Google associé à votre projet GCP, par opposition à un utilisateur spécifique.

2- Comptes d'utilisateurs - utilisés lorsque l'application a besoin d'accéder à des ressources pour le compte d'un utilisateur final.

3- Clés de l'API - généralement utilisées lors de l'appel d'API qui n'ont pas besoin d'accéder à des données privées.

+0

le GAE se trouve derrière la Identity Aware Protection qui semble exiger de suivre les documents référencés dans le message original. Je suis sous l'impression (et je n'ai pas réussi dans mes efforts) que le réglage de GOOGLE_APPLICATION_CREDENTIALS n'est pas suffisant pour IAP? – shongololo

+0

La définition des informations d'identification par défaut de l'application devrait suffire. Vous pouvez en lire plus à ce sujet [thread] (https://developers.google.com/identity/protocols/application-default-credentials). N'hésitez pas à poster les logs si vous avez du mal à les intégrer. – Kenworth

+0

est-il un moyen de définir des variables d'environnement pour les fonctions de cloud? Vous ne savez pas exactement comment définir "GOOGLE_APPLICATION_CREDENTIALS"? – shongololo