Réduire les champs d'application avec des limites d'accès aux identifiants

Cette page explique comment utiliser les limites d'accès aux identifiants pour réduire ou restreindre les autorisations IAM (Identity and Access Management) qu'un identifiant de courte durée peut utiliser.

Fonctionnement des limites d'accès aux identifiants

Pour réduire le champ d'application des autorisations, vous définissez une limite d'accès aux identifiants qui spécifie les ressources auxquelles l'identifiant éphémère peut accéder, ainsi que la limite supérieure des autorisations disponibles pour chaque ressource. Vous pouvez ensuite créer un identifiant éphémère, puis l'échanger contre un identifiant qui respecte la limite d'accès aux identifiants.

Si vous souhaitez octroyer aux membres un ensemble distinct d'autorisations pour chaque session, l'utilisation des limites d'accès aux identifiants peut s'avérer plus efficace que la création de nombreux comptes et l'octroi d'un ensemble différent de rôles à chaque compte de service. Par exemple, si l'un de vos clients doit accéder aux données Cloud Storage que vous contrôlez, vous pouvez créer un compte de service pouvant accéder à chaque bucket Cloud Storage que vous possédez, puis appliquer une limite d'accès aux identifiants qui autorise uniquement l'accès au bucket contenant les données de votre client.

Exemples de limites d'accès aux identifiants

Les sections suivantes présentent des exemples de limites d'accès aux identifiants pour les cas d'utilisation courants.

Limiter les autorisations pour un bucket

L'exemple suivant illustre une limite d'accès aux identifiants élémentaire. Elle s'applique au bucket Cloud Storage example-bucket et définit la limite supérieure des autorisations incluses dans le rôle Lecteur d'objets Storage (roles/storage.objectViewer) :

{
  "accessBoundary": {
    "accessBoundaryRules": [
      {
        "availablePermissions": [
          "inRole:roles/storage.objectViewer"
        ],
        "availableResource": "//storage.googleapis.com/projects/_/buckets/example-bucket"
      }
    ]
  }
}

Limiter les autorisations pour plusieurs buckets

L'exemple suivant illustre une limite d'accès aux identifiants qui inclut des règles pour plusieurs buckets :

  • Le bucket Cloud Storage example-bucket-1 : pour ce bucket, seules les autorisations du rôle "Lecteur des objets Storage" (roles/storage.objectViewer) sont disponibles.
  • Le bucket Cloud Storage example-bucket-2 : pour ce bucket, seules les autorisations du rôle "Créateur d'objets Storage" (roles/storage.objectCreator) sont disponibles.
{
  "accessBoundary": {
    "accessBoundaryRules": [
      {
        "availablePermissions": [
          "inRole:roles/storage.objectViewer"
        ],
        "availableResource": "//storage.googleapis.com/projects/_/buckets/example-bucket-1"
      },
      {
        "availablePermissions": [
          "inRole:roles/storage.objectCreator"
        ],
        "availableResource": "//storage.googleapis.com/projects/_/buckets/example-bucket-2"
      }
    ]
  }
}

Limiter les autorisations pour des objets spécifiques

Vous pouvez également utiliser les conditions IAM pour spécifier les objets Cloud Storage auxquels un membre peut accéder. Par exemple, vous pouvez ajouter une condition qui rend disponibles les autorisations pour les objets dont le nom commence par customer-a :

{
  "accessBoundary": {
    "accessBoundaryRules": [
      {
        "availablePermissions": [
          "inRole:roles/storage.objectViewer"
        ],
        "availableResource": "//storage.googleapis.com/projects/_/buckets/example-bucket",
        "availabilityCondition": {
          "expression" : "resource.name.startsWith('projects/_/buckets/example-bucket/objects/customer-a')"
        }
      }
    ]
  }
}

Limiter les autorisations lors de la création d'une liste d'objets

Lorsque vous répertoriez les objets dans un bucket Cloud Storage, vous appelez une méthode sur une ressource de bucket, et non sur une ressource d'objet. Par conséquent, si une condition est évaluée pour une requête de liste et que la condition fait référence au nom de ressource, ce nom identifie le bucket et non pas un objet dans le bucket. Par exemple, lorsque vous répertoriez des objets dans example-bucket, le nom de la ressource est projects/_/buckets/example-bucket.

Cette convention de dénomination peut entraîner un comportement inattendu lorsque vous répertoriez des objets. Par exemple, supposons que vous souhaitiez instaurer une limite d'accès aux identifiants qui autorise l'accès en lecture aux objets de example-bucket comportant le préfixe customer-a/invoices/. Vous pouvez essayer d'utiliser la condition suivante dans la limite d'accès aux identifiants :

Incomplete : condition qui vérifie uniquement le nom de la ressource

resource.name.startsWith('projects/_/buckets/example-bucket/objects/customer-a/invoices/')

Cette condition fonctionne pour la lecture d'objets, mais pas pour la création d'une liste des objets :

  • Lorsqu'un membre tente de lire un objet dans example-bucket comportant le préfixe customer-a/invoices/, la condition renvoie la valeur true.
  • Lorsqu'un membre essaie de répertorier des objets comportant ce préfixe, la condition renvoie la valeur false. La valeur de resource.name, à savoir projects/_/buckets/example-bucket, ne commence pas par projects/_/buckets/example-bucket/objects/customer-a/invoices/.

Pour éviter ce type de problème à l'avenir, votre condition peut utiliser resource.name.startsWith() et vérifier un attribut d'API nommé storage.googleapis.com/objectListPrefix. Cet attribut contient la valeur du paramètre prefix utilisé pour filtrer la liste des objets. Par conséquent, vous pouvez écrire une condition faisant référence à la valeur du paramètre prefix.

L'exemple suivant montre comment utiliser l'attribut d'API dans une condition. Cela permet de lire et de répertorier les objets de example-bucket comportant le préfixe customer-a/invoices/ :

Complete : condition qui vérifie le nom de la ressource et le préfixe

resource.name.startsWith('projects/_/buckets/example-bucket/objects/customer-a/invoices/')  ||
    api.getAttribute('storage.googleapis.com/objectListPrefix', '')
                     .startsWith('customer-a/invoices/')

Vous pouvez désormais utiliser cette condition dans une limite d'accès aux identifiants :

{
  "accessBoundary": {
    "accessBoundaryRules": [
      {
        "availablePermissions": [
          "inRole:roles/storage.objectViewer"
        ],
        "availableResource": "//storage.googleapis.com/projects/_/buckets/example-bucket",
        "availabilityCondition": {
          "expression":
            "resource.name.startsWith('projects/_/buckets/example-bucket/objects/customer-a/invoices/') || api.getAttribute('storage.googleapis.com/objectListPrefix', '').startsWith('customer-a/invoices/')"
        }
      }
    ]
  }
}

Avant de commencer

Avant d'utiliser les limites d'accès aux identifiants, assurez-vous de remplir les conditions suivantes :

  • Vous devez réduire le champ d'application des autorisations pour Cloud Storage uniquement, et non pour les autres services Google Cloud.

    Si vous devez réduire le champ d'application des autorisations associées à d'autres services Google Cloud, vous pouvez créer plusieurs comptes de service et attribuer un ensemble de rôles différent à chaque compte de service.

  • Vous pouvez utiliser les jetons d'accès OAuth 2.0 pour l'authentification. Les autres types d'identifiants éphémères ne sont pas compatibles avec les limites d'accès aux identifiants.

Créer un identifiant éphémère à champ d'application limité

Pour créer un jeton d'accès OAuth 2.0 avec des autorisations à champ d'application limité, procédez comme suit :

  1. Attribuez les rôles IAM appropriés à un utilisateur ou à un compte de service.
  2. Définissez une limite d'accès aux identifiants qui définit la limite supérieure des autorisations disponibles pour l'utilisateur ou le compte de service.
  3. Créez un jeton d'accès OAuth 2.0 pour l'utilisateur ou le compte de service.
  4. Échangez le jeton d'accès OAuth 2.0 contre un nouveau jeton respectant la limite d'accès aux identifiants.

Vous pouvez ensuite utiliser le nouveau jeton d'accès OAuth 2.0 à champ d'application limité pour authentifier les requêtes dans Cloud Storage.

Attribuer des rôles IAM

Une limite d'accès aux identifiants définit la limite supérieure des autorisations disponibles pour une ressource. Cette limite peut retirer des autorisations à un membre, mais ne peut pas ajouter d'autorisations que le membre ne possède pas déjà.

Par conséquent, vous devez attribuer au membre des rôles qui fournissent les autorisations nécessaires : dans un bucket Cloud Storage ou sur une ressource de niveau supérieur, comme le projet.

Supposons, par exemple, que vous ayez besoin de créer des identifiants éphémères à champ d'application limité, permettant à un compte de service de créer des objets dans un bucket :

  • Vous devez au moins octroyer au compte de service un rôle comprenant l'autorisation storage.objects.create, par exemple le rôle Créateur d'objets Storage (roles/storage.objectCreator). La limite d'accès aux identifiants doit également inclure cette autorisation.
  • Vous pouvez également attribuer un rôle comprenant davantage d'autorisations, tel que le rôle "Administrateur d'objets Storage" (roles/storage.objectAdmin). Le compte de service ne peut utiliser que les autorisations qui apparaissent à la fois dans le rôle attribué et dans la limite d'accès aux identifiants.

Pour en savoir plus sur les rôles prédéfinis pour Cloud Storage, consultez la page Rôles Cloud Storage.

Définir une limite d'accès aux identifiants

Une limite d'accès aux identifiants est un objet JSON qui contient une liste de règles de limite d'accès. Chaque règle contient les informations suivantes :

  • La ressource à laquelle s'applique la règle.
  • La limite supérieure des autorisations disponibles pour cette ressource.
  • Facultatif : une condition qui limite davantage les autorisations. Une condition inclut les éléments suivants :
    • Une expression de condition qui renvoie true ou false. Si elle renvoie true, l'accès est autorisé. Sinon, l'accès est refusé.
    • Facultatif : un titre identifiant la condition.
    • Facultatif : une description contenant des informations complémentaires sur la condition.

Si vous appliquez une limite d'accès à un identifiant éphémère, celui-ci ne peut accéder qu'aux ressources spécifiées dans la limite d'accès aux identifiants. Aucune autorisation n'est disponible pour les autres ressources.

Une limite d'accès aux identifiants peut contenir jusqu'à 10 règles de limite d'accès. Vous ne pouvez appliquer qu'une seule limite d'accès par identifiant éphémère.

Une limite d'accès aux identifiants contient les champs suivants :

Champs
accessBoundary

object

Conteneur pour la limite d'accès aux identifiants.

accessBoundary.accessBoundaryRules[]

object

Liste des règles de limite d'accès à appliquer à un identifiant éphémère.

accessBoundary.accessBoundaryRules[].availablePermissions[]

string

Liste qui définit la limite supérieure des autorisations disponibles pour la ressource.

Chaque valeur est l'identifiant d'un rôle prédéfini ou un rôle personnalisé IAM, avec le préfixe inRole:. Par exemple : inRole:roles/storage.objectViewer. Seules les autorisations associées à ces rôles sont disponibles.

accessBoundary.accessBoundaryRules[].availableResource

string

Nom de ressource complet du bucket Cloud Storage auquel la règle s'applique. Utilisez le format "//storage.googleapis.com/projects/_/buckets/bucket-name".

accessBoundary.accessBoundaryRules[].availabilityCondition

object

Facultatif. Condition limitant la disponibilité des autorisations à des objets Cloud Storage spécifiques.

Utilisez ce champ si vous souhaitez que les autorisations soient disponibles pour des objets spécifiques plutôt que pour tous les objets d'un bucket Cloud Storage.

accessBoundary.accessBoundaryRules[].availabilityCondition.expression

string

Expression de condition qui spécifie les objets Cloud Storage pour lesquels des autorisations sont disponibles.

Pour savoir comment faire référence à des objets spécifiques dans une expression de condition, consultez Attribut resource.name.

accessBoundary.accessBoundaryRules[].availabilityCondition.title

string

Facultatif. Chaîne courte qui identifie la finalité de la condition.

accessBoundary.accessBoundaryRules[].availabilityCondition.description

string

Facultatif. Informations concernant la finalité de la condition.

Pour obtenir des exemples, consultez Exemples de limites d'accès aux identifiants sur cette page.

Créez un fichier JSON qui définit la limite d'accès aux identifiants. Vous utiliserez ce fichier dans une prochaine étape.

Créer un jeton d'accès OAuth 2.0

Avant de créer un identifiant éphémère à champ d'application limité, vous devez créer un jeton d'accès OAuth 2.0 standard. Vous pouvez ensuite échanger l'identifiant standard contre un identifiant à champ d'application limité. Lorsque vous créez le jeton d'accès, utilisez le champ d'application OAuth 2.0 https://www.googleapis.com/auth/cloud-platform.

Pour créer un jeton d'accès pour un compte de service, vous pouvez terminer le flux OAuth 2.0 de serveur à serveur, ou utiliser l'API Service Account Credentials pour générer un jeton d'accès OAuth 2.0.

Pour créer un jeton d'accès pour un utilisateur, consultez la page Obtenir des jetons d'accès OAuth 2.0. Vous pouvez également utiliser OAuth 2.0 Playground pour créer un jeton d'accès pour votre propre compte Google.

Échanger le jeton d'accès OAuth 2.0

Une fois le jeton d'accès OAuth 2.0 créé, vous pouvez l'échanger contre un nouveau jeton respectant la limite d'accès aux identifiants. Pour cela, vous devez utiliser l'API Security Token Service fournie par Identity Platform.

Pour échanger le jeton d'accès, utilisez la méthode HTTP et l'URL suivantes :

POST https://sts.googleapis.com/v1beta/token

Définissez l'en-tête Content-Type de la requête sur application/x-www-form-urlencoded. Spécifiez les champs suivants dans le corps de la requête :

Champs
grant_type

string

Utilisez la valeur urn:ietf:params:oauth:grant-type:token-exchange.

options

string

Limite d'accès aux identifiants encodée en pourcentage.

requested_token_type

string

Utilisez la valeur urn:ietf:params:oauth:token-type:access_token.

subject_token

string

Jeton d'accès OAuth 2.0 que vous souhaitez échanger.

subject_token_type

string

Utilisez la valeur urn:ietf:params:oauth:token-type:access_token.

La réponse est un objet JSON qui contient les champs suivants :

Champs
access_token

string

Nouveau jeton d'accès OAuth 2.0 respectant la limite d'accès aux identifiants.

expires_in

number

Délai avant expiration du nouveau jeton d'accès, exprimé en secondes.

Ce champ n'est présent que si le jeton d'accès d'origine représente un compte de service. Lorsque ce champ n'est pas présent, le délai avant expiration du nouveau jeton d'accès est le même que celui du jeton d'accès d'origine.

issued_token_type

string

Contient la valeur urn:ietf:params:oauth:token-type:access_token.

token_type

string

Contient la valeur Bearer.

Par exemple, si la limite d'accès aux identifiants est stockée dans le fichier ./access-boundary.json, vous pouvez exécuter la commande curl suivante pour échanger le jeton d'accès. Remplacez original-token par le jeton d'accès d'origine :

curl -H "Content-Type:application/x-www-form-urlencoded" \
    -X POST \
    https://sts.googleapis.com/v1beta/token \
    -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange&subject_token_type=urn:ietf:params:oauth:token-type:access_token&requested_token_type=urn:ietf:params:oauth:token-type:access_token&subject_token=original-token" \
    --data-urlencode "options=$(cat ./access-boundary.json)"

La réponse est semblable à l'exemple suivant :

{
  "access_token": "ya29.dr.AbCDeFg-123456...",
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "token_type": "Bearer",
  "expires_in": 3600
}

Étapes suivantes