Ce sujet fait suite à l'article qui se trouve ici.
Si vous ne l’avez pas encore lu, n’attendez pas et allez-y!
Introduction
Dans l’article précédent, nous avons découvert le script amtree.sh qui permet d'exporter et d'importer les arbres d’authentification de Forgerock AM. Nous avons constaté que les données sensibles des arbres d'authentification se trouvaient à l’intérieur des fichiers JSON exportés. Nous allons alors résoudre notre problème en enlevant et ajoutant les données des fichiers JSON.
Pour répondre à notre problématique, nous allons utiliser 2 fichiers yaml. Dans cet article, nous allons préparer nos fichiers yaml et montrer comment les exploiter dans des scripts shell.
Le premier fichier contient toutes les valeurs secrètes en fonction de l'environnement
Il nous permet d'obtenir les valeurs réelles des fichiers JSON (c'est-à-dire des arbres exportés) selon leur environnement.
Exemple de configuration:
environnement1:
key1: valeur secrète pour l'attribute1 dans l'environnement1
key2: valeur secrète pour l'attribute2 dans l'environnement1
environnement2:
key1: valeur secrète pour l'attribute1 dans l'environnement2
key2: valeur secrète pour l'attribute2 dans l'environnement2
Exemple d'un fichier yaml:
dev:
urlAM: 'http://yourdevdomain.name/am'
pathAmtree: '/path/to/amtree_v2.sh'
realm: '/Test'
login: 'amadmin'
userPassword: 'devpassword'
amadminPassword: 'ampassword'
ldapServer1: '["localhost:2636"]'
amAdmin: 'uid=admin'
searchFilter: '["uid"]'
userProfile: 'uid'
minPassword: '8'
baseDn: '["dc=example,dc=com"]'
preprod:
urlAM: 'http://preprod.name/am'
userPassword: 'preprodpassword'
pathAmtree: '/path/to/amtree2.sh'
realm: '/preprodrealm'
login: 'amuser'
amadminPassword: 'passprod9'
ldapServer1: '["localhost:3637"]'
amAdmin: 'uid=admin'
searchFilter: '["uid"]'
userProfile: 'uid'
minPassword: '9'
baseDn: '["dc=example,dc=com"]'
Pour lire ce fichier yaml, nous aurons besoin de la fonction qui parse les fichiers yaml (merci stackoverflow):
function parse_yaml {
local prefix=$2
local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
sed -ne "s|^\($s\):|\1|" \
-e "s|^\($s\)\($w\)$s:$s[\"']\(.*\)[\"']$s\$|\1$fs\2$fs\3|p" \
-e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" $1 |
awk -F$fs '{
indent = length($1)/2;
vname[indent] = $2;
for (i in vname) {if (i > indent) {delete vname[i]}}
if (length($3) > 0) {
vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, $2, $3);
}
}'
}
#Parse le fichier yaml
eval "$(parse_yaml /path/to/conf.yaml "CONF1_")"
La commande parse_yaml permet de lire le fichier yaml et de valoriser des variables avec le contenu de ce fichier.
Ici, en ajoutant l'argument CONF1_ lors de l'appel à la méthode parse_yaml, on crée des variables dont le nom commence par ce préfixe. Cela nous permet par exemple de pouvoir lire plusieurs fichiers yaml dans un même script sans que les données des fichiers n'écrasent les valeurs chargées par un autre fichier yaml (on utilisera alors un prefixe différent pour chaque fichier yaml à charger).
Exemple:
on considère le fichier yaml simple ci-dessous
$ cat conf1.yaml
dev:
amadminPassword: toto
key1: value1
prod:
amadminPassword: verybigsecret
key1: value1butinprod
On définit un fichier librairie qui va contenir le script qu'on a pompé sur stackoverflow et qu'on pourra appeler dans nos différents scripts:
$ cat parse_yaml.sh
#!/usr/bin/bash
set -x
function parse_yaml {
local prefix=$2
local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
sed -ne "s|^\($s\):|\1|" \
-e "s|^\($s\)\($w\)$s:$s[\"']\(.*\)[\"']$s\$|\1$fs\2$fs\3|p" \
-e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" $1 |
awk -F$fs '{
indent = length($1)/2;
vname[indent] = $2;
for (i in vname) {if (i > indent) {delete vname[i]}}
if (length($3) > 0) {
vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, $2, $3);
}
}'
}
On fait ensuite un script simple qui va accepter en argument le chemin du fichier yaml à parser, le nom du préfixe (inutile ici puisqu'on n'a qu'un seul fichier yaml mais utile pour la suite), de l'environnement et le nom de la clé:
$ cat printValue.sh
#!/usr/bin/bash
FILEPATH=$1
ENV=$2
KEY=$3
PREFIX=CONF1_
#import script from library
source ./parse_yaml.sh
# use parse_yaml method imported from the library sourced above
eval "$(parse_yaml "$FILEPATH" "$PREFIX")"
# declare variable
variable=${PREFIX}_${ENV}_${KEY}
declare variable=$variable
# print result
echo ${!variable}
On peut alors exécuter le script et obtenir le résultat:
$ printValue.sh "/path/to/conf1.yaml" "dev" "amadminPassword"
toto
$ printValue.sh "/path/to/conf1.yaml" "prod" "amadminPassword"
verybigsecret
Note : Il faut faire attention au nombre d'espaces que nous mettons dans les fichiers yaml. En effet, plus on indente et plus on rajoute d'underscore lorsque nous écrivons dans le script, la commande qui récupère les valeurs du fichier yaml. On pourrait évidemment corriger cela en adaptant le script de parsing yaml trouvé sur stackoverflow.
Le second fichier yaml contient :
les noms des différents arbres,
des différents noms de node au sein d'un arbre
le nom des clés des nodes à anonymiser et leur valeur.
Les valeurs ne sont pas les valeurs directes mais des pointeurs. Ces valeurs étant potentiellement secrètes et surtout différentes en fonction de l'environnement de destination, nous avons décidé de donner une valeur de type "pointeur". Ce pointeur est en fait la clé du premier fichier yaml qui contient pour chaque environnement la valeur secrète de cette clé.
Exemple:
tree1:
node1:
attribute1: key1
attribute2: key2
node2:
[...]
node3:
[...]
tree2:
[...]
Pour lire ce fichier yaml, nous allons reprendre la fonction qui parse les fichiers yaml:
Et il faudra agir en 2 étapes:
1) récupérer le nom de la clé associée à la valeur à modifier (dans le 2nd fichier yaml)
2) récupérer la valeur de la clé identifiée ci-avant dans le premier fichier yaml en fonction de l'environnement:
Exemple:
On considère le même fichier conf1.yaml que ci-dessus et le 2nd fichier yaml que l'on vient de décrire:
$ cat conf2.yaml
tree1:
node1:
attribute1: amadminPassword
attribute2: key1
$ cat parse.sh #name sucks needs to be changed
#!/usr/bin/bash
FILE1PATH=$1
FILE2PATH=$2
ENV=$3
# this is just for the example, later we'll need to loop on the file content automaticaly
TREE=$4
NODE=$5
ATTRIBUTE=$6
PREFIX2=CONF2_
#import script from library
source ./parse_yaml.sh
eval "$(parse_yaml "$FILE2PATH" "$PREFIX2")"
# get key pointer for one entry in conf2 yaml file:
# declare variable
variable=${PREFIX2}_${TREE}_${NODE}_${ATTRIBUTE}
declare variable=$variable
result=${!variable}
# result contains the name of the key to be found in yaml1
# get real value from conf1 yaml file for env:
value=$(./printValue.sh "$FILE1PATH" "$ENV" "$result")
echo $value
On peut donc exécuter le script:
$ /path/to/parse.sh "/path/to/conf1.yaml" "./conf2.yaml" "dev" "tree1" "node1" "attribute1"
toto
$ /path/to/parse.sh "/path/to/conf1.yaml" "./conf2.yaml" "prod" "tree1" "node1" "attribute1"
verybigsecret
Il nous faut donc analyser tous les fichiers Json générés lors de l'export des arbres d'un realm forgerock AM, et identifier tous les paramètres qui nécessitent d'être anonymisés et/ou qui ont une valeur qui dépend de l'environnement. De cette façon, on pourra créer les 2 fichiers yaml que l'on vient de décrire.
Lorsque ces fichiers yaml sont préparés et que nous savons comment les utiliser dans des scripts shell, nous pouvons passer à la partie modification des fichiers JSON qui se trouve dans cet article.
Mayane Maman