Être conforme au RGPD dans tous vos projets est un défi quotidien, en particulier si vous gérez des données utilisateur sensibles sur vos projets. Upsun adopte une approche RGPD everywhere avec des niveaux élevés de sécurité et de conformité intégrés en standard, mais il existe des moyens de sécuriser davantage vos données sur notre PaaS lorsqu'il s'agit d'environnements de prévisualisation.
Chaque fois que vous créez une nouvelle branche Git sur un projet sur Upsun, l'environnement correspondant hérite des données (actifs et base de données) de son parent. Cela signifie que des données potentiellement sensibles de votre site web de production peuvent être exposées à l'environnement de prévisualisation.
Alors, comment s'y retrouver et s'assurer que votre application reste conforme ? Deux mots : assainissement des données. Il s'agit de l'effacement délibéré et permanent de données sensibles d'un dispositif de stockage, ce qui rend les données irrécupérables. Dans cet article, je partagerai les méthodes d'assainissement des données que vous pouvez mettre en œuvre pour les environnements de prévisualisation afin de garantir que vos données restent en sécurité à chaque étape du développement.
Nous avons quelques prérequis pour nous assurer que vous pouvez suivre les solutions et les étapes détaillées dans cet article, veuillez vous assurer que vous avez installé les éléments suivants :
Pour les besoins de cet article, nous allons nous concentrer sur l'assainissement des données de l'environnement de prévisualisation sur Symfony, cependant, les méthodes détaillées s'appliquent à tous les frameworks.
Tout au long de l'article, nous allons passer en revue les différentes méthodes disponibles pour assainir les données de l'environnement Symfony sur Upsun, 5 méthodes pour être exact que vous pouvez évaluer et choisir la meilleure pour vous. Cependant, assurez-vous que vous avez terminé l'étape de création d'une commande avant de procéder à une méthode.
Si vous connaissez déjà la méthode que vous préférez utiliser, cliquez sur le titre correspondant ci-dessous et nous vous y emmènerons directement :
Pour exécuter l'une des cinq méthodes d'assainissement des données listées ci-dessus, nous avons besoin d'un appelable pour assainir nos environnements. Il y a deux façons possibles de le faire :
Puisque nous utilisons une application Symfony Demo, nous utiliserons la seconde option. Faites ce qui suit, à partir de la branche main
de Git :
symfony composer require --dev fakerphp/faker
git add composer.json composer.lock && git commit -m "composer require --dev fakerphp bundle"
Ensuite, ouvrez votre code dans votre IDE préféré et créez une nouvelle commande Symfony, dans un fichier SRC/command/SanitizeDataCommand.php, avec ce qui suit :
<?php
/* src/Command/SanitizeDataCommand.php */
namespace App\Command;
use App\Entity\User;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Faker;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(
name: 'app:sanitize-data',
description: 'Sanitize user data (username and email).',
aliases: ['app:sanitize']
)]
class SanitizeDataCommand extends Command
{
private SymfonyStyle $io;
public function __construct(private UserRepository $userRepository, private EntityManagerInterface $entityManager)
{
parent::__construct();
}
protected function configure()
{
$this
->setDescription('This command allows you to sanitize user data (username and email).');
}
protected function initialize(InputInterface $input, OutputInterface $output): void
{
$this->io = new SymfonyStyle($input, $output);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$users = $this->userRepository->findAll();
$this->io->progressStart(count($users));
$this->entityManager->getConnection()->beginTransaction(); // suspend auto-commit
try {
/** @var User $user */
foreach ($users as $user) {
$this->io->progressAdvance();
// initialize faker
$faker = Faker\Factory::create();
$this->io->text('faking user '.$user->getUsername());
// fake user info
$user->setUsername(uniqid($faker->userName()));
$user->setEmail($faker->email());
// please adapt to your needs
}
$this->entityManager->flush();
$this->entityManager->getConnection()->commit();
$this->io->progressFinish();
} catch (\Exception $e) {
$this->entityManager->getConnection()->rollBack();
throw $e;
}
return Command::SUCCESS;
}
}
Cette commande app:sanitize-data
utilise le UserRepository et falsifie le nom d'utilisateur et l'email de l'entité par défaut User Symfony. Veuillez l'adapter à vos besoins. Ensuite, ajoutez votre code à la branche main
:
git add src/Command/SanitizeDataCommand.php && git commit -m "sanitize data command"
symfony deploy
Maintenant que votre code source contient une commande Symfony pour assainir vos données, nous allons l'utiliser manuellement dans un nouvel environnement de prévisualisation. En commençant par créer une nouvelle branche staging et en attendant que le processus se termine, comme suit :
symfony branch staging --type=staging
Ensuite, exécutez votre commande Symfony nouvellement créée sur votre environnement Upsun staging, comme indiqué ci-dessous :
symfony ssh php bin/console -e dev app:sanitize-data
Et voilà, les données de votre environnement de prévisualisation sont assainies !
Dans cette section, nous allons créer un environnement de prévisualisation, assainir ses données, et ensuite faire en sorte que tous les nouveaux environnements héritent de cet environnement de prévisualisation.
Comme mentionné au début de cet article, chaque fois que vous créez une nouvelle branche Git sur Upsun, l'environnement créé héritera des données de l'environnement parent. Cependant, il est possible de modifier l'héritage des données par défaut et de le configurer plus tard pour synchroniser les données d'un nouveau parent l'environnement de prévisualisation que nous allons créer.
Le CLI Symfony offre la possibilité de créer une branche sans parent, en utilisant l'option --no-clone-parent
, puis en définissant le parent à staging (a.k.a prévisualisation), ce qui garantit que les nouvelles branches héritent des données de l'environnement de prévisualisation. Suivez les instructions de l'étape 1 pour plus de détails sur la façon d'assainir manuellement les données de l'environnement de prévisualisation afin de garantir que toutes les branches futures héritent de données assainies et conformes au RGPD.
symfony checkout main
symfony branch dev --no-clone-parent
symfony env:info -e dev parent staging
symfony sync -e dev data
Et voilà, votre nouvel environnement de dev
est maintenant créé avec les données nettoyées de votre environnement de prévisualisation.
Plutôt que de s'appuyer sur l'héritage, il peut être souhaitable d'assainir certaines données à chaque déploiement. Dans ce cas, nous pouvons déplacer notre appel de script dans la section " hooks"
de la configuration.
Le type de hook que vous choisissez vous appartient deploy ou post_deploy, mais voici quelques points à garder à l'esprit :
Pour exécuter une commande Symfony pendant le hook post_deploy
, ajoutez ce qui suit dans votre .upsun/config.yaml
:
applications:
app:
hooks:
build: ...
deploy: ...
post_deploy: |
if [ "$PLATFORM_ENVIRONMENT_TYPE" != production ]; then
# L'assainissement de la base de données devrait avoir lieu ici (puisque ce n'est pas la production)
php bin/console -e dev app:sanitize-data
fi
Poussez ensuite votre code sur la branche main:
git checkout main && git add .upsun/config.yaml && git commit -m "add sanitize data command to post_deploy hook"
symfony deploy
Il y a une autre option qui vous permet de créer un déclencheur personnalisé qui est exécuté en réponse à certaines activités qui ont lieu sur le projet. Par exemple, lors de la synchronisation d'un environnement avec son parent, nous pourrions synchroniser les données non anonymisées du parent (par exemple, si la synchronisation se fait à partir de l'environnement de production).
Les deux composants qui permettront de réaliser cette opération sont les suivants :
Nous allons donc ajouter une intégration (script d'activité) qui répond à certains événements pour exécuter une opération d'exécution afin d'assainir les données à la volée, voir l'ajout d'une intégration d' un script d'activité ci-dessous.
Pour configurer une opération d'exécution, nous devons ajouter une nouvelle clé YAML de premier niveau dans notre fichier .upsun/config.yaml
avec ce qui suit :
applications:
app:
operations:
sanitize:
role: admin
commands:
start: |
if [ "$PLATFORM_ENVIRONMENT_TYPE" != production ]; then
# L'assainissement de la base de données devrait avoir lieu ici (puisque ce n'est pas la production)
php bin/console -e dev app:sanitize-data
fi
Poussez ensuite votre fichier vers la branche main
et déployez-le.
git checkout main
git add .upsun/config.yaml && git commit -m "add runtime operation to sanitize data"
symfony deploy
Et si vous voulez tester cette opération d'exécution manuellement, vous pouvez utiliser ce qui suit :
symfony operation:run sanitize --app=app
Upsun supporte les scripts personnalisés qui peuvent être déclenchés en réponse à n'importe quelle activité. Ce script est exécuté en dehors du contexte de l'environnement et nous devons donc recréer ce contexte pour que le script d'activité soit exécuté avec les droits nécessaires. Pour ce faire, créez un nouveau fichier src/runtime/sanitize.js
avec ce qui suit :
// src/runtime/sanitize.js
let app_container = "app";
let runtime_operation_name = "sanitize";
if (!variables.api_token) {
console.log("Variable API Token is not defined!");
console.log("Please define an environment variable with your API Token using command: ");
console.log("upsun project:curl /integrations/<INTEGRATION_ID>/variables -X POST -d '{\"name\": \"api_token\", \"value\": \"<API_TOKEN>\", \"is_sensitive\": true, \"is_json\": false}' ");
} else {
console.log("OAuth2 API Token defined");
let resp = fetch('https://auth.api.platform.sh/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: "client_id=platform-api-user&grant_type=api_token&api_token=" + variables.api_token
});
if (!resp.ok) {
console.log("Failed to get an OAuth2 token, status code was " + resp.status);
} else {
console.log("OAuth2 API TOKEN ok");
}
let access_token = resp.json().access_token;
// get current branch from activity object
let branch;
switch (activity.type) {
case 'environment.synchronize':
branch = activity.parameters.into;
break;
case 'environment.branch':
case 'environment.activate':
branch = activity.parameters.environment;
break;
}
// run runtime operation runtime_operation_name on current/targeted environment
resp = fetch("https://api.upsun.com/api/projects/" + activity.project + "/environments/" + branch + "/deployments/current/operations",
{
headers: {
"Authorization": "Bearer " + access_token
},
method: "POST",
body: JSON.stringify({"service": app_container, "operation": runtime_operation_name}),
});
if (!resp.ok) {
console.log("Failed to invoke the runtime operation, status code was " + resp.status);
} else {
console.log(runtime_operation_name + " launched");
}
}
Ce script d'activité utilise un jeton API, en tant que variable d'environnement, pour se connecter à l'environnement actuel et exécuter l'opération d'exécution définie précédemment à l'aide de l'API Upsun. Nous devons définir cette variable d'environnement pour l'intégration de notre script d'activité, et ajouter plus tard une variable d'environnement API Token.
Ensuite, poussez votre fichier sur la branche principale
et déployez-le, comme suit :
symfony checkout main
git add src/runtime/sanitize.js
git commit -m "add activity script"
symfony deploy
Trois événements Upsun doivent déclencher cette opération d'exécution :
environment.branch
),environment.synchronize
),environment.activate
) .
Pour mettre en place ces déclencheurs, utilisez cette commande dans votre terminal pour ajouter une intégration de script d'activité.
symfony integration:add --type script --file ./src/runtime/sanitize.js --events environment.branch,environment.synchronize,environment.activate --states complete --environments \*
Ajouter une variable d'environnement API Token
Tout d'abord, obtenez l'ID de l'intégration précédente en utilisant la commande suivante :
symfony integration:list
Ensuite, créez un nouveau Token API depuis la Console, gardez la valeur en main, et remplacez-la dans cette commande du terminal :
symfony project:curl /integrations/<INTEGRATION_ID>/variables -X POST -d '{"name" : "api_token", "value" : "<API_TOKEN>", "is_sensitive" : true, "is_json" : false}'
Vous pouvez vérifier que la variable a été créée avec cette commande :
symfony project:curl /integrations/<INTEGRATION_ID>/variables
Pour tester si tout a fonctionné, dans la Console ou avec le CLI, déclenchez la création d'une nouvelle branche à partir de main
, déclenchez une synchronisation, désactivez et réactivez votre environnement de prévisualisation, et vous devriez alors voir deux activités :
Si vous rencontrez un problème et que vous souhaitez déboguer l'intégration du script d'activité, vous devez utiliser la commande suivante :
symfony integration:activity:log <INTEGRATION_ID>
Lorsque vous ajoutez l'intégration de votre script d'activité, le script correspondant est ajouté en mémoire du côté d'Upsun. Cela signifie qu'à chaque fois que vous mettez à jour votre script, vous devez mettre à jour la version en cache du fichier, en utilisant la commande suivante : symfony integration:update <INTEGRATION_ID> :
symfony integration:update <INTEGRATION_ID> --file ./src/runtime/sanitize.js
Il est possible d'utiliser un script shell pour automatiser l'assainissement des données de tous vos environnements, à l'exception de la production, pour tous vos projets au sein d'une organisation apprenez-en plus sur les organisations ici. Pour utiliser ce script shell, veuillez vous assurer que toutes vos sources d'environnement de tous vos projets au sein de votre organisation contiennent la commande Symfony pour assainir les données, avant de suivre les étapes suivantes.
La première étape consiste à créer un fichier nommé fleet_sanitizer.sh avec le code suivant :
if [ -n "$ZSH_VERSION" ]; then emulate -L ksh; fi
######################################################
# fleet sanitization demo script, using the CLI.
#
# Enables the following workflow on a given project and sanitize preview environments (staging, new-feature and auto-updates environment:
# .
# └── main
# ├── staging
# | └── new-feature
# └── auto-updates
#
# Usage
# 1. source this script: `. fleet_sanitizer.sh` or `source fleet_sanitizer.sh` depending of your local machine
# 2. define ORGANIZATION var: ORGANIZATION=<organizationIdentifier>
# 3. run `sanitize_organization_data $ORGANIZATION`
######################################################
# Utility functions.
# list_org_projects: Print list of projects operation will be applied to before starting.
# $1: Organization, as it appears in console.upsun.com.
list_org_projects() {
symfony project:list -o $1 --columns="ID, Title"
}
# get_org_projects: Retrieve an array of project IDs for a given organization.
# Note: Makes array variable PROJECTS available to subsequent scripts.
# $1: Organization, as it appears in console.upsun.com.
get_org_projects() {
PROJECTS_LIST=$(symfony project:list -o $1 --pipe)
PROJECTS=($PROJECTS_LIST)
}
# get_project_envs: Retrieve an array of envs IDs for a project.
# Note: Makes array variable ENVS available to subsequent scripts.
# $1: ProjectId, as it appears in console.upsun.com.
get_project_envs() {
ENV_LIST=$(symfony environment:list -p $1 --pipe)
ENVS=($ENV_LIST)
}
# list_project_envs: Print list of envs operation will be applied to before starting.
# $1: ProjectId, as it appears in console.upsun.com.
list_project_envs() {
symfony environment:list -p $1
}
# add_env_var: Add environment level environment variable.
# $1: Variable name.
# $2: Variable value.
# $3: Target project ID.
# $4: Target environment ID.
add_env_var() {
VAR_STATUS=$(symfony project:curl -p $3 /environments/$4/variables/env:$1 | jq '.status')
if [ "$VAR_STATUS" != "null" ]; then
symfony variable:create --name $1 --value "$2" --prefix env: --project $3 --environment $4 --level environment --json false --sensitive false --visible-build true --visible-runtime true --enabled true --inheritable true -q
else
printf "\nVariable $1 already exists. Skipping."
fi
}
# Main functions.
sanitize_organization_data() {
list_org_projects $1
get_org_projects $1
for PROJECT in "${PROJECTS[@]}"; do
printf "\n### Project $PROJECT."
# get environments list
list_project_envs $PROJECT
get_project_envs $PROJECT
for ENVIRONMENT in "${ENVS[@]}"; do
unset -f ENV_CHECK
ENV_CHECK=$(symfony project:curl -p $PROJECT /environments/$ENVIRONMENT | jq -r '.status')
unset -f ENV_TYPE
ENV_TYPE=$(symfony project:curl -p $PROJECT /environments/$ENVIRONMENT | jq -r '.type')
if [ "$ENV_CHECK" = active -a "$ENV_TYPE" != production ]; then
unset -f DATA_SANITIZED
DATA_SANITIZED=$(symfony variable:get -p $PROJECT -e $ENVIRONMENT env:DATA_SANITIZED --property=value)
if [ "$DATA_SANITIZED" != true ]; then
printf "\nEnvironment $ENVIRONMENT exists and is not sanitized yet. Sanitizing data."
printf "\n"
# do sanitization here
symfony ssh -p $PROJECT -e $ENVIRONMENT -- php bin/console app:sanitize-data
printf "\nSanitizing data is finished, redeploying"
add_env_var DATA_SANITIZED true $PROJECT $ENVIRONMENT
else
printf "\nEnvironment $ENVIRONMENT exists and does not need to be sanitized. skipping."
fi
elif [ "$ENVIRONMENT" == main ]; then
printf "\nEnvironment $ENVIRONMENT is production one, skipping."
else
printf "\nEnvironment $ENVIRONMENT is not active $ENV_CHECK, skipping."
fi
done
done
}
Ensuite, en fonction de la machine sur laquelle vous voulez exécuter ce script, veuillez adapter le code à vos besoins, mais il devrait ressembler à quelque chose comme ceci :
. fleet_sanitizer.sh # or source fleet_sanitizer.sh
ORGANIZATION=<organizationIdentifier>
sanitize_organization_data $ORGANIZATION
Et juste comme ça, vos données sont assainies et vous êtes sur la bonne voie pour être en conformité avec le RGPD !
Si vous avez d'autres questions sur nos capacités en matière de sécurité et de conformité ou si vous rencontrez des problèmes avec les méthodes et/ou les étapes ci-dessus, contactez notre équipe d'assistance qui se fera un plaisir de vous aider.
Restez au courant des dernières nouvelles sur nos réseaux sociaux et nos canaux communautaires. Retrouvez-nous sur Dev.to, Reddit et Discord.