A propos de ce Blog

Ce blog a pour vocation d’exposer toutes les astuces et informations qui me semblent intéréssantes de garder et de partager à tout le monde.

Bonne lecture !



Apache2 tips & tricks

Web

Proxyfier un site et subpath différent

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
<VirtualHost *:8080>
ServerName DOMAIN.FR

SSLEngine on
SSLCertificateFile fullchain1.pem
SSLCertificateKeyFile privkey1.pem

SSLProxyEngine on
SSLProxyVerify none
SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
SSLProxyCheckPeerExpire off
ProxyPreserveHost off
ProxyRequests on

## Root path
ProxyPass / https://<https website>/
ProxyPassReverse / https://<https website>/


## Root subpath with websocket application
ProxyPass /subpath/api/websocket ws://localhost:5800/api/websocket
ProxyPassReverse /subpath/api/websocket ws://localhost:5800/api/websocket
ProxyPass /subpath/ http://localhost:5800/
ProxyPassReverse /subpath/ http://localhost:5800/
RewriteEngine on
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /subpath/(.*) ws://localhost:5800/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /subpath/(.*) http://localhost:5800/$1 [P,L]

</VirtualHost>

Proxification derière un proxy existant (Apache < 5.2)

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
<VirtualHost *:443>

ServerName DOMAIN

SSLEngine on
SSLProtocol -ALL +TLSv1.2
# New cipher suite optionnal
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
SSLHonorCipherOrder on
SSLCertificateFile "CER.pem"
SSLCertificateKeyFile "KEY.pem"
SSLCACertificateFile "ca_SSL_chain.pem"

ProxyPass / http://localhost:8081/ nocanon
ProxyPassReverse / http://localhost:8081/
RequestHeader set X-Forwarded-Proto "https"
AllowEncodedSlashes NoDecode
ProxyPass /subpath/ https://<https website>/
ProxyPassReverse /subpath/ https://<https website>/

SSLProxyEngine on
SSLProxyVerify none
SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
SSLProxyCheckPeerExpire off
ProxyRemote https://<https website>/ 'http://<proxy_url>:<proxy port>'
ProxyPreserveHost Off
ProxyRequests Off

RewriteEngine On
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /subpath/(.*) https://<https website>/$1 [P,L]

<Proxy https://<https website>/>
Order deny,allow
Allow from all
SetEnv Proxy-Chain-Auth On
RequestHeader set Proxy-Authorization "Basic <base64 encoded usrname:password>"
#SetEnv proxy-initial-not-pooled 1
</Proxy>

ErrorLog logs/error_log
CustomLog logs/access_log common

</VirtualHost>


Commands tips & tricks

Infos Reseau

1
2
IP=$(ip -4 -o a | sed -e '/^2:/!d;s/^.*inet //;s/\/.*$//g')
HOSTNAME=$(hostname -s)

Modification des hosts et dns

1
[[ $(grep -E "$IP(.*)$(hostname -s)(.*)" /etc/hosts) ]] || echo -e "$IP    $(hostname -s)" >> /etc/hosts

Verification execution en root

1
[[ $(whoami) != root ]] && { echo "ERREUR : Veuillez exécuter ce script en tant que root avec la commande sudo !"; exit 1; }

Proxy

APT:

1
echo -e "Acquire::http::Proxy \"${PROXY_URL}/\";" > /etc/apt/apt.conf.d/proxy.conf

System:

1
2
3
4
5
PROXY_URL="http://USER:PASS@HOST:PORT"
echo -e "export http_proxy='${PROXY_URL}'\n
export https_proxy='${PROXY_URL}'\n
export no_proxy=EXCLUDED_DOMAIN.fr,localhost,127.0.0.1,${HOSTNAME}" > /etc/profile.d/proxy.sh
source /etc/profile.d/proxy.sh

Maven:

1
2
mkdir -p ~/.m2
echo ${http_proxy} | tr "/:@" " " | awk '{print "<settings>\n<proxies>\n<proxy>\n<id>proxy</id>\n<active>true</active>\n<protocol>"$1"</protocol>\n<host>"$4"</host>\n<port>"$5"</port>\n<username>"$2"</username>\n<password>"$3"</password>\n</proxy>\n</proxies>\n</settings>"}' > ~/.m2/settings.xml

Curl:

1
echo "proxy = ${http_proxy}" > ~/.curlrc

Decompresser à la volée

1
2
curl -s $PACKAGE_REGISTRY/stedolan/jq/1.6/jq-1.6.tar.gz | tar xz -C /usr/bin/ && chmod +x /usr/bin/jq

Add Iptables

1
iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT && iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT && iptables-save 2> /dev/null > /etc/sysconfig/iptables

Optimisation memoire Java (java heap size)

1
-Xmx2048m -Xms512m -XX:MaxPermSize=2048m -XX:+UseConcMarkSweepGC -XX:ReservedCodeCacheSize=128m -XX:+HeapDumpOnOutOfMemoryError

Changement de cible binaire

1
2
3
4
5
update-alternatives --set php /usr/bin/php5.6

Alias alternative from vars:
echo "[[ \${php} ]] && alias php='update-alternatives --set php /usr/bin/php\${php} > /dev/null; php' \
\n[[ \${node} ]] && alias node='nvm use \${node} >/dev/null; node'" >> ~/.bashrc

Manipulation de variables

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Variable avec nom dynamique (shell): 
varname="toto"
varcontent="content"
$varname=$varcontent

name=${varname}
data=${!varname}

Variable avec nom dynamique (Groovy):
def OC_CONFIG_PATH="OC_CONFIG_AA"
evaluate("env.${OC_CONFIG_PATH} = \"--config='/tmp/$OC_CONFIG_PATH/config'\" ")
env.OC_CONFIG_PATH="$OC_CONFIG_PATH"
OC_CONFIG_PATH=\${!OC_CONFIG_PATH}
env.KUBECONFIG="/tmp/AA/oc_config_${JOB_BASE_NAME}"

Sed tricks

1
2
3
4
5
6
7
change maven version
sed -i -r -e 's#<source>(.*)</source>#<source>1.8</source>#g' -e 's#<target>(.*)</target>#<target>1.8</target>#g' pom.xml

# Export 'exist*' groovy vars outside this shell
echo "\$(env | grep -E '_exist|_version' |sed 's/^/env./')" | tee ${WORKSPACE}/vars.groovy


Find tricks

1
2
3
find ${builder.buildDir}/ -name "*.sh" -exec chmod +x {} \\;
find ${builder.buildDir}/ -name "*.sh" -exec chmod 755 {} \\;

Import propre sql

1
mysql -u root -e "source /db.sql;"

Windows Startup applications

1
%AppData%\Microsoft\Windows\Start Menu\Programs\Startup

Systemd Authorize execution from alternative PATH

1
2
semanage fcontext -a -t bin_t "/data/pgsql/bin(/.*)?"
restorecon -RF /data/pgsql/bin

Tester la presence d’un executable:

1
2
command -v <BIN>

Pousser une commande en Background et la récupérer:

1
2
3
4
5
tail -f /dev/null 	#exemple
ctrl + z #stop le process
bg # le pousse en Background
fg # pour récupere le Process


GIT- Appliquer des changements d'une branche autonome à une autre

Pour appliquer les changement d’une branche à une autre :

Comparer la liste des fichiers de master par rapport à une autre branche (demo) avec exclusion de fichier

1
git diff master..demo --stat ':(exclude)fichier1' ':(exclude)README.md'

Comparer le contenu des fichiers de master par rapport à demo

1
git diff master..demo

Appliquer les changement de master sur metro pour le fichier fichier_2.txt

1
2
git checkout demo
git checkout --patch master fichier_2.txt

-> Options:
y: accepter les changements
n: refuser les changements
a: accepter tous les changements
e: editer manuellement le fichier (si la ligne à garder commence par - il faut juste le remplacer par un espace et supprimer la ligne correspondante qui commence par un +)

Cherrypick entre plusieurs repos

  • Preparation
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    git clone https://repoA.git repoA
    git clone https://repoB.git repoB
    git clone https://repoC.git repoC

    cd repoB/
    git remote add repoA ../repoA
    git remote add repoC ../repoC

    cd ..

    cd repoC/
    git remote add repoA ../repoA
    git remote add repoB ../repoB

  • Utilisation
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Recupération de un ou plusieurs commits de repoA vers repoB

    cd repoB/
    git fetch repoA

    -> pour un seul commit:
    git cherry-pick <commit_id>

    -> pour un groupe de commits:
    git cherry-pick <first_commit>..<last_commit>

    git push

Application à grande échelle

1
for br in $(git branch --list | tr -d '*' | grep -v <SOURCE BRANCH>); do git checkout $br; git checkout --patch <SOURCE BRANCH> <FICHIER> ; done 

Authentification

1
2
3
4
5
6
7
8
9
10
11
Personal/project/group access tokens

curl "https://gitlab.example.com/api/v4/projects?private_token=<your_access_token>"

curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects"

OAuth-compliant headers:

curl --header "Authorization: Bearer <your_access_token>" "https://gitlab.example.com/api/v4/projects"

curl "https://oauth2:${CI_JOB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"

Pull and push back in Gitlab-ci

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
update_tree_on_git:
stage: update_tree_on_git
tags:
- <target runner>
variables:
# Save Git token in CI/CD variables as GIT_PUSH_TOKEN
CI_JOB_TOKEN: $GIT_PUSH_TOKEN
before_script:
# Set Git info
- git config user.email "${GITLAB_USER_EMAIL}"
- git config user.name "${GITLAB_USER_NAME}"
- git remote add origin_push https://oauth2:${CI_JOB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git
script:
- **make some stuff**
after_script:
# Save changes
- git add <files> && git commit -m "<commit message>"
- git push origin_push HEAD:${CI_COMMIT_BRANCH} -o ci.skip # Prevent triggering pipeline again

Save git credential

1
git config --global credential.helper store

Configuration réseau sous Ubuntu 18.04

Sur Ubuntu 18, la gestion du réseau est maintenant géré par Netplan au lieu de NetworkManager
La gestion DNS peut être gérée par systemd ou intégrée à Netplan

Gestion des DNS avec systemd

Editer le fichier resolved:

1
2
3
4
5
6
7
8
9
10
11
vi /etc/systemd/resolved.conf

[Resolve]
DNS=8.8.8.8 8.8.4.4
#FallbackDNS=
#Domains=
#LLMNR=no
#MulticastDNS=no
#DNSSEC=no
#Cache=yes
#DNSStubListener=yes

puis redemarer le service:

1
systemctl restart systemd-resolved.service

Gestion reseau avec netplan

Editer le fichier de configuration de netplan

1
2
vi /etc/netplan/50-cloud-init.yaml

Exemple de configuration:

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
network:
version: 2
ethernets:
ens192:
dhcp4: false
addresses: [163.172.123.123/24, 212.83.123.123/32]
gateway4: 163.172.123.1
ens33:
dhcp4: true
dhcp4-overrides:
route-metric: 50
nameservers:
addresses: [8.8.8.8]
wifis:
wlp2s0b1:
dhcp4: no
dhcp6: no
addresses: [192.168.0.21/24]
gateway4: 192.168.0.1
nameservers:
addresses: [192.168.0.1, 8.8.8.8]
access-points:
"network_ssid_name":
password: "**********"

puis appliquer les modifications:

1
sudo netplan apply

Menu en Shell

Create Usage function

1
2
3
4
5
6
7
8
9
10
USAGE(){
echo "-e <env>, --env=<value> Précise l'environnement cible (--env=dev, --env=rec, --env=prod, etc)"
echo "-u, --uninstall Désinstalle le Chatbot"
echo "-d, --delete-volumes Supprime les données persistantes du Chatbot (peut être cumulé avec l'option --uninstall)"
echo "-p, --pull Télécharge seulement les images nécessaires au Chatbot"
echo "-h, --help Affiche cette page d'aide"
echo
echo "Si aucune option n'est précisée, le script lance un déploiement du Chatbot en mode interactif"
echo
}

Using Menu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
OPTS=$(getopt --options "e:,h,u,d,p" --long "env:,help,uninstall,delete-volumes,pull" --name "$0" -- "$@")
[[ $? != 0 ]] && { echo "Echec à la lecture des options !" >&2 ; exit 1; }
eval set -- "$OPTS"

while true ; do
case "$1" in
-e | --env ) ENV="$2"; shift 2 ;;
-p | --pull ) DOWNLOAD_IMAGES; EXIT_STATUS=true; shift ;;
-u | --uninstall ) CLEAN_ENV; EXIT_STATUS=true; shift ;;
-d | --delete-volumes ) CLEAN_PERSISTENT_DATA; shift ;;
-h | --help ) USAGE; exit 1;;
-- ) shift; break ;;
* ) USAGE; exit 1;;
esac
done
[[ $EXIT_STATUS ]] && exit 0;

Importation certificat SSL (ubuntu/centos)

Importation certificat SSL (ubuntu/centos)

1
2
3
4
5
6
7
8
9
10
11
12
13
if [ "$(. /etc/os-release && echo "$ID")" = "ubuntu" ]; then
CERT_DIR="/usr/local/share/ca-certificates"
cp $APACHE_PATH/ssl/certs/cacert.pem $CERT_DIR/ca.crt
---- OR ----
openssl s_client -connect HOST:PORT -showcerts </dev/null 2>/dev/null | sed -e '/-----BEGIN/,/-----END/!d' | sudo tee -a $CERT_DIR/HOST.crt
update-ca-certificates --fresh
else
CERT_DIR="/etc/pki/ca-trust/source/anchors"
cp $APACHE_PATH/ssl/certs/cacert.pem $CERT_DIR/ca.crt
---- OR ----
openssl s_client -connect HOST:PORT -showcerts </dev/null 2>/dev/null | sed -e '/-----BEGIN/,/-----END/!d' | sudo tee -a $CERT_DIR/HOST.crt
update-ca-trust
fi

Redimensionnement d'un disque vmware suite à une augmentation de disque alloué

Redimensionnement d’un disque d’une machnie virtuelle sous vmware

Après avoir augmenté l’espace alloué d’un disque il se peut que la vm ne voit pas son disque agrandit.

Il faut donc taper les commandes suivantes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
gdisk /dev/sda
Command (? for help): w
Warning! Secondary header is placed too early on the disk! Do you want to
correct this problem? (Y/N): y
Do you want to proceed? (Y/N): y
reboot now
fdisk /dev/sda
Command (m for help): p
Command (m for help): d
Partition number (1,2, default 2): 2
Command (m for help): n
Partition number (2-128, default 2): 2
First sector (4096-209715166, default 4096): 4096
Last sector, +sectors or +size{K,M,G,T,P} (4096-209715166, default 209715166): 209715166
Do you want to remove the signature? [Y]es/[N]o: y
Command (m for help): w
reboot now
resize2fs /dev/sda2

Docker tips & tricks

Install latest docker & docker-compose

1
2
curl -sSL https://get.docker.com/ | sh
curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

Configure proxy for docker

1
2
3
4
mkdir -p /etc/systemd/system/docker.service.d
PROXY_URL='http:/USER:PASS@PROXY:PORT'
echo -e "[Service] \nEnvironment=\"HTTPS_PROXY=${PROXY_URL}\"\nEnvironment=\"NO_PROXY=localhost,127.0.0.1,${HOSTNAME}\"" > /etc/systemd/system/docker.service.d/http-proxy.conf
systemctl daemon-reload && systemctl restart docker

Change data directory & activate logrotate

1
2
3
mkdir -p /etc/docker/ /data
echo -e "{\"data-root\": \"/data/docker\",\"log-driver\": \"json-file\", \"log-opts\": { \"max-size\": \"100m\",\"max-file\": \"5\" }}}" | tee /etc/docker/daemon.json
systemctl daemon-reload && systemctl enable docker && systemctl restart docker

Clean by filter orphan images or regex container

1
2
docker rmi $(docker images -q -f 'dangling=true') 2> /dev/null || true
docker rm -f $(docker ps -aq -f 'name=NAME*') 2> /dev/null || true

Import many images from directory, apply tag by date and latest, and push to registry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Livraisons="2018-10-10 2019-01-21 2019-03-21 2019-03-26 2019-04-10 2019-04-11"

for livraison in $Livraisons;do
for image in $(ls $IMAGES_PATH/$livraison/*.tar); do
name=$(docker load -q -i $image | awk '{print $3}');
newtag=$(basename $name | sed -e "s/:.*$/:$(basename $(dirname $image))/g")
latesttag=$(basename $name | sed -e 's/:.*$/:latest/g')
docker tag $name $DOCKER_REGISTRY/$newtag;
docker tag $name $DOCKER_REGISTRY/$latesttag
docker push $DOCKER_REGISTRY/$newtag
docker push $DOCKER_REGISTRY/$latesttag
docker rmi $name >/dev/null;
echo -e "\n==> image $DOCKER_REGISTRY/$newtag imported\n";
done
done

Dockerfile exemple multi-tool container

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
### Input variables ##
ARG http_proxy
ARG https_proxy
ARG no_proxy

## put arg as env for futur use
ENV http_proxy=$http_proxy
ENV https_proxy=$https_proxy
ENV no_proxy=$no_proxy

# System Env
ENV TERM=xterm
ENV LC_ALL=C.UTF-8
ENV DEBIAN_FRONTEND=noninteractive

# Package Env
ENV INSTALL="apt-utils software-properties-common vim dos2unix sqlite3 mysql-server mysql-client libpcre3-dev apache2 php-pear php-pecl-http maven"
ENV INSTALL_PHP="5.5 5.6 7.0 7.2"
ENV INSTALL_PHP_MODULES="xml mysql http mcrypt sqlite3 mbstring curl cli gd dev intl xsl memcache memcached zip apc"
ENV INSTALL_JAVA="openjdk-8-jdk openjdk-8-jre"
ENV INSTALL_NODEJS="8 9 10 11 12"

# Download others packages
RUN apt-get update -y && apt-get install -y software-properties-common apt-transport-https lsb-release

# Download the signing key for php repo
RUN wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg

# Add repo for php packages
RUN echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list && apt-get update -y

# Add nvm for nodejs managing
RUN wget -O install.sh https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh; bash install.sh

# Installing packages
# System packages
RUN /bin/bash -c 'for packages in ${INSTALL[@]};do apt-get install -y ${packages} ;done'

# Php & php modules packages
RUN /bin/bash -c 'for php_version in ${INSTALL_PHP[@]};do apt-get install -y php${php_version} ; for php_modules in ${INSTALL_PHP_MODULES[@]};do apt-get install -y php${php_version}-${php_modules} ;done ;done'

# Java packages
RUN /bin/bash -c 'for java_version in ${INSTALL_JAVA[@]};do apt-get install -y ${java_version} ;done'

# Nodejs packages
RUN /bin/bash -c 'source ~/.profile 2>/dev/null; for node_version in ${INSTALL_NODEJS[@]};do nvm install ${node_version} ;done'



Pipeline Devops pour Let's Encrypt

Web

Montage d’une pipeline DevOps pour Let’s Encrypt

Afin de faciliter mes futurs projecs et de pouvoir les sécuriser, j’ai décider d’utiliser Let’s Encrypt de façon automatique.

Il était déja possible d’utiliser Let’s Encrypt pour certifier un nom de domain ou sous domaine, aujourd’hui il est possible de certifier via un wilcard, ce qui nous permet de couvrir le totalité des nom de domaines / sous-domaines et futurs sous-domaines sous la forme *.mon-domaine.fr

Integration de Let’s Encrypt dans une pipeline

Avec ce concept de wildcard, il est maintenant possible de gérer un seul certificat pour l’ensemble des domaines, ce qui automatise déjà la création d’un certificat par projet.

Pour automatiser cette génération, j’ai intégré Let’s Encrypt dans une pipeline Gitlab-ci, ce qui permet de nous passer d’un serveur pour le faire.

=> Lien vers le job de la pipeline de génération

Cette Pipeline est appellée automatiquement tous les 1er du mois avec Gitscheldule (semblable à cron).

Déploiement et livraison des certificats

Avec cette Pipeline Gitlab-Ci, une partie n’est pas couverte, c’est celle du chargement du certificat sur le serveur de l’application.

Deux cas sont étudiées:

  • Site hebergé avec Git-Pages, comme ce porte folio, les certificats doivent donc être poussés vers Gitlab directement
  • Site hebergé sur un serveur classique, il faudra donc pouvoir récupérer les fichiers directement et les faire portés par apache/nginix etc..

Cas Gitlab-Pages

Gitlab Fournit une Api avec laquelle il est possible de pousser directement les certificats sur un domaine associé.

J’ai donc développé un script qui parcours la totalités de mes projets Gitlab, cherche si ils contiennent des Gitlab-Pages et si ils sont attachés à un nom de domaine.

Dans ce cas le certificat est récupéré de la pile de cache de la pipeline Let’Encrypt et est poussé directement par api sur le domaine du Gitlab-page.

=> Lien vers le script

Cas récuperation des livrables

Afin de pouvoir récupérer les certificats pour pouvoir les exploiter sur un autre serveur, j’ai utilisé les artifacts dans la pipeline Gitab-Ci.

La fonction artifacts permet d’archiver un contenu présent dans un job Gitlab-ci et de le proposer en téléchargement une fois la pipeline terminée.

Cette fonction peut être utile dans de nombreux cas, par exemple récuperer les sources/livrables d’une complation, récuperer des logs etc..

Il est donc possible de récuperer les certificats génénerés via l’interface Gitlab ou par API, c’est cette dernière qui nous intéresse.

Effectivement il suffit d’ajouter la commande suivante dans une tache cron mensuelle sur le serveur en question, et les certificats seront automatiquement actualisés

1
curl --output certificats.zip -L --header "PRIVATE-TOKEN: $TOKEN" https://gitlab.com/api/v4/projects/8641028/jobs/artifacts/master/download?job=certbot-renew

=> Lien vers le job de la pipeline de déploiement