Vous avez déjà fouillé les permissions GCP pour trouver le rôle qui conviendrait à votre besoin ?

C’est ultra chiant, il faut cliquer partout et on y passe un temps fou et si on faisait les choses autrement ?

Patrick Bruel qui joue au Poker

Vous savez que je suis un grand fan de la ligne de commande, parce que c’est particulièrement pratique pour automatiser et scripter toute sorte de choses, c’est mon outil de productivité principal. Je mange dedans, je dors dedans, je bricole des alias, des fonctions pour me faire gagner du temps. Je code avec vim parce que pour mon besoin ça suffit largement et me permet d’être à l’aise en toute circonstance. Je peux travailler avec la même efficacité sur mon poste local ou en environnement tout pourri avec un VPN, un rebond SSH et pas d’interface graphique.

Petite explication pour les moins techniques d’entre vous, SSH est un protocole qui permet de lancer des commandes à distance via un protocole réseau. Un rebond SSH est une technique qui consiste à utiliser une première machine sécurisée pour connecter une seconde machine non accessible autrement que par cette première machine.

Alors oui vous allez me dire, gna gna gna Vim c’est tout moche, tu perds en productivité sans les outils d’aide au dev, ça fait du code vilain, bouh c’est pas bien.

mais taisez vous Alain Finkielkraut

Je dirais 2 choses:

  • glorifier un IDE (environnement de développement) c’est une bataille d’église stérile, comme la glorification d’un langage de programmation plutôt qu’un autre.
  • utilise un linter pour toute ton équipe dans un CI/CD et tu verras que les problèmes de cohérences de code et de style seront gommés.

Je viens à peine de commencer mon article que je suis déjà en train de digresser, ça ne présage rien de bon pour la suite. 🙄

Donc mon besoin c’est que régulièrement je dois chercher des rôles GCP pour autoriser des comptes de services à réaliser des actions. Comme j’évite au maximum de faire des rôles custom parce que c’est une tannée ensuite à maintenir, J’utilise la barre de recherche de la console IAM GCP pour trouver le rôle qui correspond le mieux à mon besoin.

Mais c’est long et très casse pied.

the office, Jim meurt d’ennui

J’aimerai bien avec un outil en ligne de commande qui fasse ça pour moi, pour éviter à chaque fois:

  • connexion à l’interface + (parfois) code MFA dans le téléphone
  • cliquer sur un projet
  • cliquer sur IAM
  • cliquer sur rôles
  • copier-coller la permission demandée dans la barre de recherche
  • trouver le rôle qui pourrait convenir le mieux (le moins possible de permission non utilisée dans ma tâche)
  • cliquer sur le détail
  • analyser les permissions et la fonction du rôle (pour éviter de donner des permissions trop fortes pour le besoin)

Ma première idée, c’est de faire un script python qui me liste les rôles, leurs permissions et m’affiche de quoi prendre une décision sur le rôle que je veux utiliser.

Python (animal) vérifie du code Python

idéalement:

  • le nom du rôle
  • le nombre de permissions associé au rôle
  • la liste des permissions associées (pour voir s’il n’y a pas un truc un peu trop permissif dans la liste)

Pour ce script j’ai deux idées qui me viennent à l’esprit:

  • solution naïve, on liste tout et on trie avec python ensuite
  • étude de la barre de recherche GCP et on essaye de faire la même chose, mais dans un script.

Dumb and Dumber

Allons y pour la solution débile en premier ! Ça risque d’être très bof en termes de perf, mais c’est la solution qui semble la plus simple à appréhender, donc on fonce et on regarde si on prend des murs.

film dumb and dumber

algo

on boucle sur les rôles 
    on boucle sur les permissions dans les rôles
        si une permission match avec celle qu'on cherche, on affiche :
        - le nom du rôle
        - le nombre de permissions
        - les permissions

L’intérêt d’afficher au fil de l’eau permet de s’affranchir de la lecture de toutes les permissions et de gagner un peu de temps sur le traitement, tout en évitant de surcharger la RAM.

On part du principe qu’on va devoir manipuler des jeux de donnée structurés, donc on oublie l’idée de faire un script Bash.

On pourrait utiliser des commandes Gcloud en mode sale dans du Python, mais tant qu’à faire autant directement utiliser le SDK de Google.

https://github.com/googleapis/google-cloud-python

Je voulais lire la doc, mais en fait non, ça m’a soulé :)

C’est compliqué de trouver ce que je cherche dans la documentation officielle donc j’ai cherché un bout de code qui fait déjà une partie du boulot.

panda énervé renverse la table

https://stackoverflow.com/questions/59755317/list-roles-in-specific-project-in-google-cloud-platform-using-python

En reformatant un peu le script, j’obtiens mon résultat, ça tombe en marche, c’est encore un peu baveux, mais on obtient le résultat.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#!/usr/bin/env python3
# coding:utf8

import argparse

from googleapiclient import discovery
from oauth2client.client import GoogleCredentials


def parse_args():
    clean_args = {}
    parser = argparse.ArgumentParser(
        description="the permission you want to find in role"
    )
    parser.add_argument(
        "-p",
        "--permission",
        type=str,
        help="an integer for the accumulator",
        required=True,
    )

    args = parser.parse_args()

    clean_args["permission"] = args.permission.strip()
    return clean_args


def fetch_all_gcp_roles_and_match_permission(permission):

    print(f"searching for permission {permission} in all GCP roles")

    credentials = GoogleCredentials.get_application_default()
    gcp_iam_api = discovery.build("iam", "v1", credentials=credentials)
    request = gcp_iam_api.roles().list()

    while request:
        response = request.execute()
        for role in response.get("roles", []):
            print_detailed_role_by_role_name(
                permission=permission, role_name=role["name"], gcp_iam_api=gcp_iam_api
            )
        request = gcp_iam_api.roles().list_next(
            previous_request=request, previous_response=response
        )


def print_detailed_role_by_role_name(permission, role_name, gcp_iam_api):
    request_detailed = gcp_iam_api.roles().get(name=role_name)
    detailed_role = request_detailed.execute()
    try:
        if permission in detailed_role["includedPermissions"]:
            print(
                f"""\n{detailed_role["name"]}  {detailed_role["title"]}
found {len(detailed_role['includedPermissions'])} permission(s) for this role
{detailed_role["description"]}\n"""
            )
    except:
        pass # no permission on this role

def main():
    clean_args = parse_args()
    fetch_all_gcp_roles_and_match_permission(clean_args["permission"])


if __name__ == "__main__":
    main()

Par contre, une énorme faiblesse de mon script, il faut faire toute la liste des permissions et c’est coûteux en termes de temps de récupération de données.

Six minutes d’exécution à chaque fois, et sans espoir de compression du temps dans cette configuration, puisqu’on téléphone à GCP par internet à chaque fois.

meme David Goodenough

C’est inacceptable si on doit en rechercher plusieurs à la suite, c’est beaucoup trop long et en l’état on va plus vite avec le dashboard GCP.

En l’état ça ne rend pas vraiment le service.

Mais on ne va pas s’arrêter là, on rebranche quelques neurones et on regarde comment c’est fait sur GCP.

Hypothèse sur le fonctionnement de la barre de recherche

En regardant comment c’est fait dans la console développeur GCP, on apprend très peu de choses.

On voit transiter des JSON inexploitables et pas tellement intéressants dans notre cas.

En gros j’ai ouvert une console développeur et j’ai essayé d’analyser les flux réseaux et jouant avec la barre de recherche des rôles.

Ce que j’en conclus, c’est que la recherche et le filtre des informations sont gérés directement dans le JavaScript dans le navigateur et que l’ensemble des rôles est probablement préchargé.

Donc en gros pas trop exploitable pour nous, c’est la grosse loose Milouse.

milouse joue tout seul au tape cul

Comme le dashboard de Google est protégé par MFA et le mot de passe et que le front est parfois un peu bancal, aucune chance de faire une interface stable avec du PhantomJS pour simuler un utilisateur humain.

Impossible avec mes connaissances actuelles de fouiller l’API consommé par frontend et d’en faire un truc exploitable.

Du coup je reste sur mon utilisation de l’API et j’adapte ma solution autrement.

Stocker des données localement ?

Je ne vais surement pas installer un moteur d’indexation ou une base de données pour un besoin aussi ridicule.

Mais ce que je crois avoir deviné sur la barre de recherche de Google me donne l’idée de précharger les données localement.

Comme la récupération des données via API REST est à mon avis la raison principale de mes lenteurs de script, si on stocke les infos localement, on gagne forcément du temps.

Jammy c’est pas sorcier, c’est malin

Pas besoin d’avoir des permissions 100% à jour tout le temps, sauf si on utilise des produits tout neufs avec des rôles et permissions en cours de stabilisation.

Mais dans ce cas on recharge le fichier de permission et on est bon.

On garde la même boucle pour le moment, on verra ensuite si on trouve une librairie un peu plus fine pour faire de la recherche rapide si le temps d’exécution est encore long.

Objectif:

  • script plus rapide
  • utilisation offline
  • pas de dépendances overkill

ça nous donne un truc comme ça

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
#!/usr/bin/env python3
# coding:utf8

import argparse
import os
import yaml
from googleapiclient import discovery
from oauth2client.client import GoogleCredentials

STORAGE_FILE = f"{os.path.dirname(os.path.realpath(__file__))}/roles.yaml"


def parse_args():
    clean_args = {}
    parser = argparse.ArgumentParser(
        description="the permission you want to find in role"
    )
    parser.add_argument(
        "-p",
        "--permission",
        type=str,
        help="the permission you want to match",
        required=True,
    )

    parser.add_argument(
        "-s",
        "--store",
        type=bool,
        help="refresh store data",
        default=False,
        required=False,
    )

    args = parser.parse_args()

    clean_args["permission"] = args.permission.strip()
    clean_args["store"] = args.store
    return clean_args


def store_all_roles_and_permission_if_needed(store):
    if not os.path.isfile(STORAGE_FILE) or store:
        print("refreshing local file permission")
        refresh_storage_file()
    else:
        print("use local file to search permission")


def refresh_storage_file():
    roles_dict = {}
    credentials = GoogleCredentials.get_application_default()
    gcp_iam_api = discovery.build("iam", "v1", credentials=credentials)
    request = gcp_iam_api.roles().list()

    while request:
        response = request.execute()
        for role in response.get("roles", []):
            request_detailed = gcp_iam_api.roles().get(name=role["name"])
            detailed_role = request_detailed.execute()
            roles_dict[detailed_role["name"]] = {
                "title": detailed_role["title"],
                "nb_permissions": len(detailed_role.get("includedPermissions", [])),
                "permissions": detailed_role.get("includedPermissions", []),
                "description": detailed_role.get("description", ""),
            }
            request = gcp_iam_api.roles().list_next(
                previous_request=request, previous_response=response
            )
    with open(STORAGE_FILE, "w", encoding="UTF-8") as storage_file:
        storage_file.write(yaml.dump(roles_dict))


def load_roles_dict():
    with open(STORAGE_FILE, "r", encoding="UTF-8") as storage_file:
        return yaml.safe_load(storage_file.read())


def match_permission_with_local_file(permission, roles_dict):
    print(f"searching for permission {permission} in all GCP roles")
    for role_name in roles_dict:
        try:
            if permission in roles_dict[role_name].get("permissions"):
                print(
                    f"""\n{role_name}  {roles_dict[role_name]["title"]}
found {roles_dict[role_name]['nb_permissions']} permission(s) for this role
{roles_dict[role_name].get("description")}\n"""
                )

        except TypeError:
            pass


def main():
    clean_args = parse_args()
    store_all_roles_and_permission_if_needed(store=clean_args["store"])
    roles_dict = load_roles_dict()
    match_permission_with_local_file(
        permission=clean_args["permission"], roles_dict=roles_dict
    )


if __name__ == "__main__":
    main()

Une fois le fichier local généré, on arrive à un temps d’exécution d’environs 13 secondes, ce qui est un énorme gain par rapport au 6 minutes précédente.

Ce n’est pas quelques chose à mettre entre toutes les mains, vous êtes littéralement satellisé !

Vega missile, ça pousse (meme)

C’est clairement bien plus intéressant que de chercher l’information dans l’interface GCP.

Par contre l’ergonomie du format de sortie est un peu pourrie, je vois bien un truc ou deux améliorables.

meilleur affichage des résultats

Première chose qu’on va mettre en place, on va tenter de trier les rôles par nombres de permissions par rôles, dans l’ordre décroissant. Les plus intéressants à afficher en dernier puisque dans la console le dernier afficher sera le premier visible.

C’est pas forcément le rôle le plus restrictif qui convient pour toutes nos solutions, mais c’est une métrique importante dans notre choix de décision. Ça pourrait être intéressant d’afficher les permissions des trois rôles les plus intéressants de la liste pour aider à la réflexion. J’avais affiché l’ensemble des permissions de tous les rôles au tout début, mais c’était complètement illisible.

Ça implique du coup de lister les permissions trouvées et de les réorganiser en RAM par la suite. J’en profite pour améliorer le moyen d’authentification et utilisé le gcloud default login. Il faut s’authentifier avec Gcloud avec cette commande pour enregistrer l’authentification par defaut dans Gcloud localement.

1
gcloud auth application-default login

On évite de trainer un Service Account pour rien et on utilise sont compte perso sur sa machine perso.

Du coup on bricole encore un peu le script et on arrive à un truc comme ça !

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/usr/bin/env python3
# coding:utf8

import argparse
import os
import yaml
from googleapiclient import discovery
from oauth2client.client import GoogleCredentials

from pprint import pformat

STORAGE_FILE = f"{os.path.dirname(os.path.realpath(__file__))}/roles.yaml"


def parse_args():
    clean_args = {}
    parser = argparse.ArgumentParser(
        description="the permission you want to find in role"
    )
    parser.add_argument(
        "-p",
        "--permission",
        type=str,
        help="the permission you want to match",
        required=True,
    )

    parser.add_argument(
        "-s",
        "--store",
        type=bool,
        help="refresh store data",
        default=False,
        required=False,
    )

    args = parser.parse_args()

    clean_args["permission"] = args.permission.strip()
    clean_args["store"] = args.store
    return clean_args


def store_all_roles_and_permission_if_needed(store):
    if not os.path.isfile(STORAGE_FILE) or store:
        print("refreshing local file permission")
        refresh_storage_file()
    else:
        print("use local file to search permission")


def refresh_storage_file():
    roles_dict = {}
    credentials = GoogleCredentials.get_application_default()
    gcp_iam_api = discovery.build("iam", "v1", credentials=credentials)
    request = gcp_iam_api.roles().list()

    while request:
        response = request.execute()
        for role in response.get("roles", []):
            request_detailed = gcp_iam_api.roles().get(name=role["name"])
            detailed_role = request_detailed.execute()
            roles_dict[detailed_role["name"]] = {
                "title": detailed_role["title"],
                "nb_permissions": len(detailed_role.get("includedPermissions", [])),
                "permissions": detailed_role.get("includedPermissions", []),
                "description": detailed_role.get("description", ""),
            }
            request = gcp_iam_api.roles().list_next(
                previous_request=request, previous_response=response
            )
    with open(STORAGE_FILE, "w", encoding="UTF-8") as storage_file:
        storage_file.write(yaml.dump(roles_dict))


def load_roles_dict():
    with open(STORAGE_FILE, "r", encoding="UTF-8") as storage_file:
        return yaml.safe_load(storage_file.read())


def match_permission_with_local_file(permission, roles_dict, matched_dict):
    print(f"searching for permission {permission} in all GCP roles")
    for role_name in roles_dict:
        try:
            if permission in roles_dict[role_name].get("permissions"):
                matched_dict[role_name] = roles_dict[role_name]
        except TypeError:
            pass
    return matched_dict


def format_and_print_result(matched_dict):

    # sort and reverse roles by nb permissions
    reversed_permissions_nbs = (
        tuple(
            sorted(
                [(matched_dict[role]["nb_permissions"], role) for role in matched_dict]
            )
        )
    )[::-1]

    # print all roles - last 3 roles without permissions
    for i in range(0,len(reversed_permissions_nbs)-3):
        role_name = reversed_permissions_nbs[i][1]
        print(
            f"""\n{role_name}  {matched_dict[role_name]["title"]}
found {matched_dict[role_name]['nb_permissions']} permission(s) for this role
{matched_dict[role_name].get("description")}"""
            )

    for i in range(len(reversed_permissions_nbs)-3, len(reversed_permissions_nbs)):
        role_name = reversed_permissions_nbs[i][1]
        print (
            f"""\n{role_name}  {matched_dict[role_name]["title"]}
found {matched_dict[role_name]['nb_permissions']} permission(s) for this role
{matched_dict[role_name].get("description")}\n
{pformat(matched_dict[role_name].get("permissions"))}\n
"""
        )

def main():
    clean_args = parse_args()
    store_all_roles_and_permission_if_needed(store=clean_args["store"])
    roles_dict = load_roles_dict()

    matched_dict = {}
    matched_dict = match_permission_with_local_file(
        permission=clean_args["permission"],
        roles_dict=roles_dict,
        matched_dict=matched_dict,
    )
    format_and_print_result(matched_dict)


if __name__ == "__main__":
    main()

et le résultat d’exécution plutôt pas dégeux !

pikachu lunette de soleil et Sacha au téléphone au bord de la piscine

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
./gcp_permission_finder.py --permission="artifactregistry.dockerimages.list"
use local file to search permission
searching for permission artifactregistry.dockerimages.list in all GCP roles

roles/owner  Owner
found 5126 permission(s) for this role
Full access to most Google Cloud resources. See the list of included permissions.

roles/editor  Editor
found 4732 permission(s) for this role
View, create, update, and delete most Google Cloud resources. See the list of included permissions.

roles/viewer  Viewer
found 2284 permission(s) for this role
View most Google Cloud resources. See the list of included permissions.

roles/iam.securityAdmin  Security Admin
found 1268 permission(s) for this role
Security admin role, with permissions to get and set any IAM policy.

roles/iam.securityReviewer  Security Reviewer
found 1083 permission(s) for this role
Security reviewer role, with permissions to get any IAM policy.

roles/composer.worker  Composer Worker
found 506 permission(s) for this role
Worker access to Composer. Intended for service accounts.

roles/visualinspection.serviceAgent  Visual Inspection AI Service Agent
found 265 permission(s) for this role
Grants Visual Inspection AI Service Agent admin roles for accessing/exporting training data, pushing containers artifacts to GCR and ArtifactsRegistry, and Vertex AI for storing data and running training jobs.

roles/cloudfunctions.serviceAgent  Cloud Functions Service Agent
found 104 permission(s) for this role
Gives Cloud Functions service account access to managed resources.

roles/cloudbuild.serviceAgent  Cloud Build Service Agent
found 71 permission(s) for this role
Gives Cloud Build service account access to managed resources.

roles/ml.serviceAgent  AI Platform Service Agent
found 57 permission(s) for this role
AI Platform service agent can act as log writer, Cloud Storage admin, Artifact Registry Reader, BigQuery writer, and service account access token creator.

roles/containeranalysis.ServiceAgent  Container Analysis Service Agent
found 52 permission(s) for this role
Gives Container Analysis API the access it needs to function

roles/cloudbuild.builds.builder  Cloud Build Service Account
found 47 permission(s) for this role
Can perform builds

roles/serverless.serviceAgent  Cloud Run Service Agent
found 41 permission(s) for this role
Gives Cloud Run service account access to managed resources.

roles/compliancescanning.ServiceAgent  Compliance Scanning Service Agent
found 36 permission(s) for this role
Gives Compliance Scanning the access it needs to analyze containers and VMs for compliance and create occurrences using the Container Analysis API

roles/run.serviceAgent  Cloud Run Service Agent
found 31 permission(s) for this role
Gives Cloud Run service account access to managed resources.

roles/artifactregistry.admin  Artifact Registry Administrator
found 27 permission(s) for this role
Administrator access to create and manage repositories.

roles/containerscanning.ServiceAgent  Container Scanner Service Agent
found 22 permission(s) for this role
Gives Container Scanner the access it needs to analyzecontainers for vulnerabilities and create occurrences using the Container Analysis API

roles/artifactregistry.repoAdmin  Artifact Registry Repository Administrator
found 22 permission(s) for this role
Access to manage artifacts in repositories.

['artifactregistry.aptartifacts.create',
 'artifactregistry.dockerimages.get',
 'artifactregistry.dockerimages.list',
 'artifactregistry.files.get',
 'artifactregistry.files.list',
 'artifactregistry.packages.delete',
 'artifactregistry.packages.get',
 'artifactregistry.packages.list',
 'artifactregistry.repositories.deleteArtifacts',
 'artifactregistry.repositories.downloadArtifacts',
 'artifactregistry.repositories.get',
 'artifactregistry.repositories.list',
 'artifactregistry.repositories.uploadArtifacts',
 'artifactregistry.tags.create',
 'artifactregistry.tags.delete',
 'artifactregistry.tags.get',
 'artifactregistry.tags.list',
 'artifactregistry.tags.update',
 'artifactregistry.versions.delete',
 'artifactregistry.versions.get',
 'artifactregistry.versions.list',
 'artifactregistry.yumartifacts.create']



roles/artifactregistry.writer  Artifact Registry Writer
found 18 permission(s) for this role
Access to read and write repository items.

['artifactregistry.aptartifacts.create',
 'artifactregistry.dockerimages.get',
 'artifactregistry.dockerimages.list',
 'artifactregistry.files.get',
 'artifactregistry.files.list',
 'artifactregistry.packages.get',
 'artifactregistry.packages.list',
 'artifactregistry.repositories.downloadArtifacts',
 'artifactregistry.repositories.get',
 'artifactregistry.repositories.list',
 'artifactregistry.repositories.uploadArtifacts',
 'artifactregistry.tags.create',
 'artifactregistry.tags.get',
 'artifactregistry.tags.list',
 'artifactregistry.tags.update',
 'artifactregistry.versions.get',
 'artifactregistry.versions.list',
 'artifactregistry.yumartifacts.create']



roles/artifactregistry.reader  Artifact Registry Reader
found 13 permission(s) for this role
Access to read repository items.

['artifactregistry.dockerimages.get',
 'artifactregistry.dockerimages.list',
 'artifactregistry.files.get',
 'artifactregistry.files.list',
 'artifactregistry.packages.get',
 'artifactregistry.packages.list',
 'artifactregistry.repositories.downloadArtifacts',
 'artifactregistry.repositories.get',
 'artifactregistry.repositories.list',
 'artifactregistry.tags.get',
 'artifactregistry.tags.list',
 'artifactregistry.versions.get',
 'artifactregistry.versions.list']

On obtient un résultat rapide, avec les “meilleurs résultats” en bas de la page.

Dans cet exemple on trouve un résultat parfaitement pertinent, on demande une permission pour lister les images dockers. La première permission est un rôle en lecture seule du registre d’artefact (pour les images docker et d’autres trucs), si on ne fait que de la lecture c’est celui qu’il nous faut.

Viennent ensuite les permissions d’écriture et d’administration qui sont les deux rôles croissant au dessus de la permission de lecture.

Les autres roles ensuite englobes des produits qui utilise les containers mais avec beaucoups plus de permissions.

À voir ce que ça peut donner sur des permissions un peu plus exotiques (avec des tigres et des Dramas), mais on a sans mal un résultat plus rapide et efficace que la console GCP !

portrait de Joe Exotic et Carole Baskin (Tiger king)

Inconvénient, il faut recharger le fichier local de temps à autre pour ajouter les nouvelles permissions. C’est à cela que sert le paramètre supplémentaire –store, on peut de cette façon recharger le fichier yaml local qui contient la liste des permissions.

Conclusion

Une fois encore, j’aurais dû faire ce script depuis un moment ! À chaque fois c’est pareil, je subis un peu le truc et je finis toujours par l’automatiser. Alors que finalement c’est un script plutôt simple en définitive, et une très bonne raison de ne plus aller cliquer dans la console GCP :) J’ai aussi trouvé un prétexte parfait pour farfouiller les APIs de Google, qui sont finalement très accessibles et bien documentées.

Les gens pressés comme moi profitent des exemples de code qui fonctionnent, ça compte énormément pour tester des trucs rapidement. Le dashboard GCP pour moi est pourtant le meilleur qui soient, mais quand on a besoin de bombarder, rien ne vaut le code.

prison break, j’ai pas le temps

Une bonne preuve que parfois l’augmentation des perfs ça tiens sur des astuces un peu bête et par forcément sur de l’optimisation de code à outrance ou un changement de langage réputé plus rapide. Une bonne morale, des fois les maths ça ne sert à rien et un marteau piqueur fait tout aussi bien le taf :)

Ah oui et cette fois ci j’ai mis le code dans github, c’est plus simple pour vous, prenez le et faite ce que vous voulez avec !

https://github.com/helldrum/gcp-role-finder

À bientôt pour une prochaine connerie :)