🛠️ Cas d'usage avec CISO Assistant et Grist
Présentation du cas d'usage
Ciso Assistant m'a permis de supprimer en grande partie la segmentation documentaire en intégrant l'analyse des risques issue du travail formidable de David Soria fondateur d'Astar (il n'a pas besoin de publicité, j'en profite juste pour vous le conseiller dans vos projets Cybersécurité).
J'ai commencé ensuite à intégrer les mesures de sécurité en m'appuyant sur le guide d'hygiène de l'ANSSI. Pas évident de dresser les mesures de sécurité from scratch même si je partais d'un squelette. Entre temps l'équipe de Ciso Assistant a sorti récemment la version détaillée du guide avec les mesures de sécurités correspondantes 🥹😍.
Cela m'a obligé de revoir et repenser ma copie concernant l'approche par la conformité. Concernant la segmentation documentaire, un point noir résidé sur la gestion des preuves : comment suivre l'ensemble des preuves ? Manuellement ? C'est ce que j'ai fait au début, mais le nombre de preuves et les preuves elles-même évoluant puis le fait que j'occupe plusieurs fonctions, je me suis vite rendu compte que ce n'était pas possible. C'est à ce moment-là que j'ai pris pleinement conscience d'utiliser l'API de Ciso Assistant dont voici mon premier cas d'usage.
Dans le cadre de mon travail, je me dois de tenir un tableau des permissions d'accès au serveur de fichiers à jour. Jusqu'à présent cela se faisait sur des fichiers excel. Cependant nous arrivions à une segmentation documentaire des preuves trop importante et ne facilitant pas le suivi pour mes équipes et moi-même. J'ai donc basculé cette gestion sur Grist.
Cette application présente plusieurs fonctionnalités dont celle de fournir une API tout comme Ciso Assistant.
L'idée a donc été d'exploiter les deux API afin de verser automatiquement le tableau des permissions d'accès au serveur de fichiers issu de Grist dans la preuve correspondante sur Ciso Assistant.
Environnement
- Système GNU/Linux
- VM Proxmox VE faisant tourner Docker
- Conteneur docker Grist
- Conteneur docker Ciso Assistant
- Python
Principe de fonctionnement
Voici le principe dans les grandes lignes :
- Le script pyhton est lancé par une tâche CRON,
- il intérroge l'API de Grist afin de récupérer le tableau des permissions au format xlsx,
- il nettoie ce fichier en ne conservant que les colonnes et feuilles pertinentes,
- il exporte dans Ciso Assistant le fichier sur la preuve concernée.
Contrainte: Le script ne doit exporter le fichier que si des permissions ont été crées ou mises à jour. Pour cela, les permissions sont déclarées dans un tableau sur Grist contenant une colonne nommée Export_Ciso_Assistant
de type booléen (True/False). Si ce champs est à False, cela indiquera au script de mettre à jour le fichier sur Ciso Assistant. Une fois le fichier importé dans Ciso Assistant, le script mettra alors ce champs à True. Si aucun champs n'est à False, alors le script n'exporte pas le tableau depuis Grist.
Disclaimer
Je ne suis pas dev, il y aura certainement des choses à redire/à améliorer sur le script.
Je débute sur Grist.
Généralement à un problème informatique, plusieurs solutions possibles. Il se peut donc que la mienne ne soit pas la meilleure.
Tableau des permissions sur Grist
Les entêtes du tableau sont celles-ci :
Seule la colonne "Export Ciso Assistant" ne sera pas reprise dans le fichier xlsx généré plus tard.
Script Python
import requests
import json
from openpyxl import load_workbook
import logging
logging.basicConfig(level=logging.INFO)
## FONCTIONS ###################################################
def main():
try:
# Étape 1 : on récupère la table contenant les permissions
liste_permissions = recup_permissions(gristUrl, gristDocument, gristTable, gristToken)
if not liste_permissions or len(liste_permissions.get('records', [])) == 0:
logging.info("Pas de nouvelles permissions détectées")
return
logging.info(f"Présence de {len(liste_permissions['records'])} permission(s) à traiter.")
# Étape 2 : On exporte le document au format Excel depuis Grist
if not export_permissions(gristUrl, gristDocument, gristToken, fichierExcel):
logging.error("Échec de l'export des permissions vers Grist.")
return
logging.info("Export du document au format Excel depuis Grist réussi.")
# Étape 3 : On nettoie le fichier Excel en supprimant les informations non pertinantes
if not nettoyage_tableau_excel(fichierExcel):
logging.error("Échec du nettoyage du fichier Excel.")
return
logging.info("Nettoyage du fichier Excel réussi.")
# Étape 4 : On se connecter à Ciso et on importe le fichier excel nettoyé dans la preuve concernée
cisoToken = connect_ciso(LoginCisoUrl, cisoUser, cisoPassword)
if not cisoToken:
logging.error("Connexion à Ciso échouée.")
return
if not import_ciso(cisoEvidenceUrl, cisoToken, fichierExcel):
logging.error("Échec de l'importation dans Ciso.")
return
logging.info("Importation dans Ciso réussie.")
# Étape 5 : On met à jour le statut de la colonne Export_Ciso_Assistant sur Grist
for permission in liste_permissions['records']:
update_ciso_statut(gristUrl, gristDocument, gristTable, gristToken, permission['id'])
logging.info("Mise à jour de la colonne Export_Ciso_Assistant sur Grist réussie.")
except Exception as e:
logging.exception(f"Une erreur inattendue est survenue : {e}")
# Récupération de l'ensemble des enregistrements de la table requêtée
def recup_permissions(url,doc,table,token):
try:
url = f"{url}{doc}{table}"
params = {
"filter": '{"Export_Ciso_Assistant": [false]}'
}
headers = {
"accept": "application/json",
"Authorization": f"Bearer {token}"
}
reponse = requests.get(url, headers=headers, params=params)
return reponse.json()
except ValueError:
print("Échec de la connexion:", response.status_code, response.text)
exit()
# Export du tableau excel des permissions
def export_permissions(url,doc,token,fichier):
url = f"{url}{doc}/download/xlsx"
headers = {
"accept": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"Authorization": f"Bearer {token}",
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
with open(fichier, "wb") as file:
file.write(response.content)
return True
else:
return False
def nettoyage_tableau_excel(fichier):
try:
# Charger le fichier Excel existant
wb = load_workbook(fichier)
# Suppression des onglets non nécessaire du fichier récupéré
onglets_to_delete = ["📁 Répertoires","Procedure","🛠️Paramètres","Poles","Services","🛡️Comptes avec privilèges"]
for onglet in onglets_to_delete:
if onglet in wb.sheetnames:
ws = wb[onglet]
wb.remove(ws)
# Suppresion de la colonne "Export Ciso Assistant" contenue sur la feuille "Permissions"
if "Permissions" in wb.sheetnames:
ws = wb["Permissions"]
# Trouver l'index de la colonne contenant "Export Ciso Assistant"
for col in ws.iter_cols(min_row=1, max_row=1): # Parcourir les colonnes de la première ligne
if col[0].value == "Export Ciso Assistant":
ws.delete_cols(col[0].column) # Supprimer la colonne
break
else:
print("La feuille 'Permissions' n'existe pas dans le fichier.")
# Suppresion des colonnes "AgentBis" et "DUP" contenue sur la feuille "👥 Utilisateurs et groupes"
if "👥 Utilisateurs et groupes" in wb.sheetnames:
ws = wb["👥 Utilisateurs et groupes"]
liste_colonnes = ["AgentBis","DUP"]
for colonne in liste_colonnes:
for col in ws.iter_cols(min_row=1, max_row=1): # Parcourir les colonnes de la première ligne
if col[0].value == colonne:
ws.delete_cols(col[0].column) # Supprimer la colonne
break
else:
print("La feuille '👥 Utilisateurs et groupes' n'existe pas dans le fichier.")
wb.save(fichier)
return True
except ValueError:
return False
def connect_ciso(url,user,password):
login_url = url
login_headers = {
'accept': 'application/json',
'Content-Type': 'application/json'
}
login_data = {
"username": user,
"password": password
}
response = requests.post(login_url, headers=login_headers, json=login_data)
if response.status_code == 200:
token = response.json().get('token')
return token
else:
print("Échec de la connexion:", response.status_code, response.text)
exit()
# Connexion à Ciso Assistant pour récupérer le token
def import_ciso(url,token,fichier):
upload_url = url
upload_headers = {
'Authorization': f'Token {token}',
'accept': 'application/json',
'Content-Type': 'document',
'Content-Disposition': f'attachment; filename={fichier}'
}
file_path = fichier
with open(file_path, 'rb') as file:
response = requests.post(upload_url, headers=upload_headers, data=file)
if response.status_code == 200:
return True
else:
return False
# Mise à jour du champs Export_Ciso_Assistant dans Grist
def update_ciso_statut(url,doc,table,token,idPermission):
try:
url = f"{url}{doc}{table}"
datas = {"records": [ { "id": idPermission, "fields": { "Export_Ciso_Assistant": "true" } } ]}
headers = {
"accept": "*/*",
"Content-Type": "application/json",
'Authorization': f'Bearer {token}'
}
reponse = requests.patch(url, headers=headers, json=datas)
return True
except ValueError:
return False
## VARIABLES ###################################################
# Grist
gristUrl = "https://grist.domaine.lab/api"
# Token à récupérer dans les paramètres du compte utilisateur
gristToken = "3a2ad19e24gsf6598422d6sdsdsdsd500f5f72a8f468" #
gristDocument = "/docs/qD6gPyXKtrWvHCKWQbKtCG"
gristTable = "/tables/Permissions/records"
fichierExcel = "grist-acces-srv-fichiers.xlsx"
# CISO Assistant
cisoUrl = "https://ciso.domaine.lab"
LoginCisoUrl = f"{cisoUrl}/api/iam/login/"
cisoUser = "LOGIN@MAIL.COM"
cisoPassword = "PASSWORD"
cisoEvidenceUrl = f"{cisoUrl}/api/evidences/b828ad59-4a2a-4b80-9c0a-a79e5b1d6733/upload/"
## SCRIPT ######################################################
if __name__ == "__main__":
main()
Les émojis contenus dans le script ne sont pas des interprétations. Grist permet de mettre des emojis rendant l'interface plus sympa.
python3 getPermissions2.py
INFO:root:Présence de 10 permission(s) à traiter.
INFO:root:Export du document au format Excel depuis Grist réussi.
INFO:root:Nettoyage du fichier Excel réussi.
INFO:root:Importation dans Ciso réussie.
INFO:root:Mise à jour de la colonne Export_Ciso_Assistant sur Grist réussie.
Une fois le script exécuté, je retrouve bien mon fichier sur CISO Assistant.
Le nom du fichier a été modifié par CISO Assistant étant donné que c'était la énième preuve que j'importais dans l'application.
Conclusion
Voilà pour ce premier cas d'usage qui est aussi un premier jet qui j'espère vous inspirera et vous convaincra de passer à CISO Assistant et vous tentera de tester Grist, si ce n'est pas déjà le cas.
Dans ce cas, j'utilise Python, mais j'envisage à termes de basculer sur une application de workflows d'automatisation comme Active Pieces qui est open-source puisqu'il semblerait que n8n ne soit plus open source. Cela éviterai un peu trop de boulot sur la maintenance des scripts. Cela dépendra également de l'intégration de CISO Assistant dans Active Pieces 😉.
J'ai commencé à travailler également sur la conformité des règles d'accès. En effet, le tableau des permissions étant déclaratif, il me semble nécessaire de vérifier l'état des permissions déclarées, là aussi de manière automatique.
J'en profite pour remercier les équipes de Grist et de CISO Assistant de rendre accessible leurs applications à tout le monde. Et merci particulièrement à Abderrahmane Smimite pour ton soutien et ta reconnaissance de l'humble contributeur que je suis (et je dis ça sans fausse modestie).