Aller au contenu

Messages recommandés

Posté(e) (modifié)

1. Préambule

Ce guide a pour but de permettre à tout un chacun de centraliser la gestion de ses conteneurs sur une seule et même instance Docker, et ce de manière sécurisée.
Sur une distribution Linux classique ou même Windows il est possible d'exposer une instance Docker via TCP, donc la rendre accessible sur un port de la machine hôte, 2375 en non-sécurisé, 2376 par TLS.
De manière générale c'est quelque chose qu'on évite, car Docker possède des privilèges élevés sur sa machine hôte, c'est donc une source de contamination potentiellement dévastatrice.
En prenant soin de placer un certain nombre de garde-fous, et en maîtrisant les points de sécurisation abordés dans les tutoriels références du forum (en premier lieu celui sur la sécurisation), l'idée devient tout à fait envisageable.

Il y a deux avantages majeurs à cette méthode :
- Elle est applicable à n'importe quelle machine, votre NAS, un PC sous Linux, un micro-processeur type Raspberry, un VPS, un serveur dédié, etc...
- Elle permet de s'affranchir des limitations de certains OS, typiquement DSM.

On peut tout à fait exposer par TCP l'instance Docker d'un NAS Syno sans passer par un proxy sauf qu'à chaque redémarrage les modifications sont effacées et il faudra de nouveau modifier la ligne de commande de démarrage du démon Docker.
Un script pourrait sûrement tout à fait se charger de la tâche, reste que l'on touche à des fichiers systèmes sensibles, je suis partisan du fait de garder un DSM "stock" pour éviter des problèmes lors des mises à jour et des incompatibilités/bugs qui en découlent fréquemment.

2. Prérequis

  • Savoir protéger ses périphériques (pare-feu)
  • Savoir établir une connexion suffisamment sécurisée entre deux machines
  • Savoir rediriger un port
  • Avoir des bases concernant Docker (voir tutoriel introductif)
  • Savoir se connecter en SSH à un périphérique
  • Avoir défini un nom de domaine entièrement qualifié (FQDN en anglais - Fully Qualified Domain Name) pour l'instance Docker cible

Difficulté : Moyenne

3. Sécurisation

Pour garantir un certain degré de sécurité, l'idée va être d'exposer le socket Docker via un proxy, ce qui sera réalisé par un conteneur sur l'hôte cible, avec lequel nous établirons une connexion TLS depuis l'instance centralisatrice.
Sa localisation peut être quelconque : sur le même réseau local, accessible à distance par HTTPS ou encore par VPN.
Le choix de la solution et la sécurisation de l'environnement sont à votre discrétion et découlent des pré-requis stipulés ci-dessus.

4. Portainer

Pour faciliter la visualisation de mes instances Docker (ou environment) et mes conteneurs, j'utilise l'application Portainer sur la machine qui va servir de centre névralgique pour toutes mes instances. Elle a l'avantage de fournir une interface claire, efficace et intuitive.
(Notons qu'il est tout à fait possible de s'en passer et de se cantonner à de la ligne de commande, voir documentation Docker dont le lien est donné plus loin).
Un fichier docker-compose.yml adapté aux NAS pour Portainer :

version: '2.1'
services:

   portainer:
      image: portainer/portainer-ce
      container_name: portainer
      network_mode: bridge
      volumes:
         - /volume1/docker/portainer/data:/data
         - /var/run/docker.sock:/var/run/docker.sock
      ports:
         - 9000:9000
      restart: unless-stopped

Puis on se place dans le dossier où se trouve le fichier docker-compose.yml précédent et on tape la commande suivante :

docker-compose up -d

Ou on passe par lignes de commande :

docker create \
--name=portainer \
--net=bridge \
--restart=unless-stopped \
-v /volume1/docker/portainer/data:/data \
-v /var/run/docker.sock:/var/run/docker.sock \
-p 9000:9000 \
portainer/portainer-ce

Puis on tape :

docker start portainer

Remarque :
/volume1/docker/portainer/data est un emplacement adapté pour un NAS Synology, il faudra auparavant créer le dossier portainer et data dans le dossier partagé docker dans File Station.

La première fois qu'on se connecte (via http://IP:9000), on est amené à choisir un login et un mot de passe admin.
Ce faisant on arrive sur un écran demandant de choisir l'environment qu'on souhaite configurer, il faut choisir local et valider successivement les écrans.

On arrive rapidement à un écran de la sorte :

portainer_endpoint.thumb.png.914f04064788e7e31b587b2f829dcd72.png

Je ne rentre pas dans le détail de l'utilisation de Portainer, on trouve des tutoriels relativement bien faits sur Youtube et Google, et c'est de toute façon assez simple à prendre en main :
https://www.youtube.com/watch?v=GNG6PDFxQyQ
(à 1:36 on parle précisément de ce qu'on cherche à faire dans ce guide)
https://domopi.eu/ameliorer-la-gestion-de-vos-containers-docker-avec-portainer/

5. Méthodes au choix

Plusieurs méthodes sont disponibles :

5-A. Portainer-agent

Avantage :
- Facile et rapide à mettre en place

Inconvénients :
- Utilisation de certificats client/serveur auto-signés
- Fonctionne uniquement avec Portainer

Utilisation recommandée : réseau local ou VPN

Pour le mettre en place sur le serveur distant qu'on souhaite superviser via notre instance centralisatrice, on passera par Docker-compose ou par lignes de commande :

  • Par docker-compose :
version: '2.1'
services:

   portainer-agent:
      image: portainer/agent
      container_name: portainer-agent
      network_mode: bridge
      volumes:
         - /var/lib/docker/volumes:/var/lib/docker/volumes
         - /var/run/docker.sock:/var/run/docker.sock
      ports:
         - 9001:9001
      restart: unless-stopped

Puis :

docker-compose up -d
  • Par lignes de commande :
docker create \
--name=portainer-agent \
--net=bridge \
--restart=unless-stopped \
-v /var/lib/docker/volumes:/var/lib/docker/volumes \
-v /var/run/docker.sock:/var/run/docker.sock \
-p 9001:9001 \
portainer/agent

Puis :

docker start portainer-agent

Remarques :

  • On monte le dossier contenant les volumes Docker, dont le principe est repris dans le tutoriel introductif. Cela permettra de parcourir ces dossiers depuis l'interface Portainer, très pratique pour aller télécharger certains fichiers internes au conteneur (logs, fichiers de configuration, etc...) :
    • On remplace /var/lib/docker/volumes:/var/lib/docker/volumes par /volume1/@docker/volumes:/var/lib/docker/volumes si l'agent est installé sur un NAS Synology.
    • On remplace /var/lib/docker/volumes:/var/lib/docker/volumes par /volume1/.@plugins/AppCentral/docker-ce/docker_lib/volumes:/var/lib/docker/volumes si l'agent est installé sur un NAS Asustor (merci à @MilesTEG1)
  • On expose le port 9001 du conteneur sur le port 9001 de l'hôte (ou un autre port quelconque non utilisé).
  • Si la machine cliente est derrière un routeur, on pense à faire la redirection du port 9001 vers celle-ci. Et que son pare-feu autorise les connexions ce port.

Rendez-vous au paragraphe 6-A pour l'ajout de notre agent dans l'interface Portainer.

5-B. Portainer Edge agent

A venir.

5-C. Liaison TLS + Proxy

5-C-1. Préambule

Avantage :
- Sécurisé par certificat client/serveur auto-signé (mais généré par vous-même donc auto-signé).
- Utilisable dans toutes les utilisations envisageables d'une liaison TLS entre un serveur et un client, pas seulement dans le cadre de Docker.

Inconvénients :
- Plus long à mettre en place

Utilisation recommandée : réseau local / VPN ou serveur distant

Ici je vais prendre l'exemple d'un VPS OVH entrée de gamme.

5-C-2. Préparation

La première étape consiste à se connecter en SSH avec l'utilisateur de notre choix sur la cible (le VPS en l'occurence pour moi) et de définir la variable HOST avec le FQDN de notre machine.
Dans mon cas, j'utilise le nom de domaine que j'ai défini dans ma zone DNS OVH via un enregistrement A vers l'IP fixe de mon VPS.
De manière générale, le FQDN peut être local ou externe, peu importe, car c'est un certificat auto-signé que nous allons générer pour l'atteindre, le tout étant que la résolution du FQDN soit adaptée à l'environnement (je ne peux pas utiliser vps.local si je passe par une résolution externe).
Cela peut donc se faire comme moi avec un FQDN externe, si vous souhaitez gérer l'instance Docker d'un raspberry de votre réseau local, il peut s'agir de son enregistrement A correspondant dans votre serveur DNS local, ou simplement ce que vous avez renseigné dans le fichier /etc/hosts de votre instance centralisatrice.

Pour l'exemple :

HOST=target.ndd.tld

En tapant :

echo $HOST

On doit obtenir le FQDN défini ci-avant.

5-C-3. Certificat serveur

On va créer un dossier docker_tls dans le /home de notre utilisateur et on commence à suivre (pas bêtement, mais presque) les consignes de la documentation Docker, les étapes étant parfaitement décrites, je ménage vos touches Alt+Tab ou vous évite un torticolis si vous êtes en double écran en recopiant les étapes ici. 😉 Si vous souhaitez plus de détail sur l'explication de chaque étape, Rendez-vous sur la page.

mkdir docker_tls
cd docker_tls

Puis on poursuit avec les commandes fournies par le guide :

openssl genrsa -aes256 -out ca-key.pem 4096

central_docker_1.png.5ac79e10db5f0368c9c6c3eb5b1070e5.png

openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem

central_docker_2.png.fff449d18dced052615c09ab6335df0e.png

openssl genrsa -out server-key.pem 4096

openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr

[[[ ATTENTION Il se peut que vous obteniez l'erreur suivante :

central_docker_3.png.a2c2f11076e7270eb726a1fd59a18919.png

Il suffit dans ce cas-là de créer le fichier manquant :

touch .rnd

et de recommencer ]]]

Arrive le passage le plus subtil, il va falloir définir les IP et les FQDN capables d'accéder à cette instance, ça se présente sous cette forme :

echo subjectAltName = DNS:,IP: >> extfile.cnf

Évidemment, il va falloir renseigner les valeurs de manière exhaustive, sous peine de devoir recommencer depuis cette étape.
Ce passage permet de renforcer la sécurisation également, car tout nom de domaine (et donc IP associée) et IP non déclarés se verront refuser l'accès au socket (Connection refused sur Portainer).
Il faudra au minimum ajouter $HOST (que l'hôte puisse accéder à sa propre instance, ça ne mange pas de pain), la boucle locale 127.0.0.1, et le FQDN et/ou l'IP de notre instance centralisatrice.

Un exemple, où j'autorise en plus par exemple :
- l'
IP fixe publique de mon instance centralisatrice 51.25.152.236 (fictive) (en cas d'un problème de résolution DNS, je peux toujours y accéder)
- l'enregistrement A qui lui est associé central.ndd.tld (ça peut également être mon dynhost pour les IP dynamiques)
- l'IP privée de mon instance centralisatrice lorsque connectée au serveur VPN de mon VPS 10.0.0.2

echo subjectAltName = DNS:$HOST,DNS:central.ndd.tld,IP:51.25.152.236,IP:10.0.0.2,IP:127.0.0.1 >> extfile.cnf

On poursuit :

echo extendedKeyUsage = serverAuth >> extfile.cnf

openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf

.central_docker_4.png.38c30f73c7f93d01a448a37c2d309a8b.png

5-C-4. Certificat client

Par facilité, on va rester sur la machine hôte et créer les certificats et la clé privée client.

openssl genrsa -out key.pem 4096

openssl req -subj '/CN=client' -new -key key.pem -out client.csr

echo extendedKeyUsage = clientAuth > extfile-client.cnf

openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile-client.cnf

central_docker_5.png.e2742573ca74e6a472bd624bbf2e605d.png

rm -v client.csr server.csr extfile.cnf extfile-client.cnf

chmod -v 0400 ca-key.pem key.pem server-key.pem

chmod -v 0444 ca.pem server-cert.pem cert.pem

5-C-5. Récapitulatif

Si tout s'est bien déroulé, un petit ls -lt devrait donner ceci :

central_docker_6.png.9fb05590107b10dd515f46700a7d5dc8.png

5-C-6. Proxy

Il nous faut maintenant créer le conteneur servant de proxy, dont voici la page GitHub de l'image.
Un modèle de fichier docker-compose :

version: '2.1'
services:

    docker-socket-proxy:
        image: sjawhar/docker-socket-proxy
        container_name: docker-socket-proxy
        network_mode: bridge
        volumes:
            - /path/to/the/server/certs:/run/secrets:ro
            - /var/run/docker.sock:/var/run/docker.sock:ro
        ports:
            - 2376:2376
        restart: unless-stopped

Puis :

docker-compose up -d

Ou par lignes de commande :

docker create \
--name=docker-socket-proxy \
--net=bridge \
--restart=unless-stopped \
-v /path/to/the/server/certs:/run/secrets:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-p 2376:2376 \
sjawhar/docker-socket-proxy

Puis :

docker start docker-socket-proxy

Remarques :
- /path/to/the/server/certs représente le dossier dans lequel vous allez placer vos certificats, cela dépend de l'OS de la machine cliente. Sur une distribution Linux classique, ça peut être dans le /home d'un utilisateur, dans /root ou partout ailleurs.

Parmi les huit fichiers restants, trois nous intéressent pour ce conteneur : ca.pem, server-key.pem, server-cert.pem
Ces trois fichiers doivent se trouver dans le chemin que vous aurez choisi pour /path/to/the/server/certs, pour ma part j'ai créé un sous-dossier certs dans le dossier du conteneur et je les y ai copiés.
Le port 2376 est à ouvrir (et rediriger si besoin) sur la machine cible, évidemment. Et que le pare-feu de la machine, s'il y en a un, autorise les connexions sur ce port.

Une fois le conteneur démarré, si tout va bien les logs du conteneur n'affichent rien, on le vérifie en tapant :

docker logs docker-socket-proxy

6. Ajout de l'environment sur Portainer

6-A. Portainer agent

On va ajouter l'agent en cliquant simplement dans Environment dans le menu latéral, puis + Add environment.

On sélectionne Agent, puis on complète de cette manière :

portainer_endpoints_agent.thumb.PNG.69d1fd83366a8dca49ca789443776058.PNG

L'IP 192.168.1.50 est un exemple évidemment, on pense à préciser le port.
On peut éventuellement ajouter des tags pour trier facilement les environments (utile si on supervise beaucoup de machines).

Si vous cliquez sur Home dans le menu latéral, vous voyez maintenant votre instance distante avec le statut Up.

6-B. Proxy

On commence par rapatrier les trois fichiers utiles pour le client : ca.pem (le même que pour le serveur), cert.pem et key.pem. La sélection des fichiers se fera par une fenêtre de parcours, comme sur une interface graphique classique Linux ou Windows.
Pour ceux que ça n'aide pas, j'ai utilisé scp et ai mis les fichiers sur mon bureau Linux (attention à la majuscule, c'est -P, pas -p)

scp -P <port-SSH> ca.pem cert.pem key.pem toto@central.ndd.tld:~/Bureau

Pour Windows, vous pouvez récupérer les fichiers avec WinSCP par exemple via connexion SSH.

Le serveur est maintenant accessible, il ne reste plus qu'à se connecter à Portainer et ajouter l'environment. Dans le menu déroulant de gauche, on clique sur Environment, puis + Add environment. Puis on complète de la sorte, en adaptant évidemment à ses propres données :

central_docker_7.thumb.png.08eef1e838a409dddcb75e92a4c4d5c6.png

On notera la sélection des certificats et de la clé en bas de la page.
On clique ensuite sur "Add environment".

Si vous cliquez sur Home dans le menu latéral, vous voyez maintenant votre instance distante avec le statut Up.

7. Utilisation de Github comme source des fichiers compose

Il est possible de créer un dépôt personnel sur Github (ou tout autre logiciel git-compatible type Gitea, etc... notamment auto-hébergé 😉) afin d'y stocker ses fichiers compose, au lieu de les stocker sur la machine cible ou en utilisant les outils de Portainer. L'avantage est d'avoir accès depuis n'importe où, avec le niveau de sécurité que l'on a établi pour sa connexion à Github (2FA par exemple), à l'ensemble des fichiers de configuration de ses applications.

Mieux, si on modifie un fichier, Portainer le détectera ou en sera informé et recréera le conteneur avec ses nouveaux paramètres.

Pour cela, on va devoir dans un premier temps créer un token d'accès à Github pour Portainer.

7-A. Génération d'un personal acces token

Tout d'abord, il faut créer un compte Github. Une fois ceci fait, on clique sur son portrait, puis Settings :

tuto_portainer_github_1.png.e208defcaafb5e89ad43ae8ce4e85801.png

Puis Developer settings tout en bas du menu à la gauche de l'écran -> Personal access tokens -> Generate new token :

tuto_portainer_github_2.thumb.png.4a60627ea897dec29ed31f18876b70b8.png

Ce sont, de mes tests, les permissions minimales à accorder via ce token à Portainer pour pull correctement les fichiers lors du déploiement de la pile (stack).

On clique ensuite sur Generate token.

ON NOTE BIEN LE TOKEN, SI ON LE PERD IL N'Y A AUCUN MOYEN DE LE RECUPERER.
Il faudra alors en recréer un.

On va maintenant créer un dépôt pour stocker nos fichiers.

7-B. Création d'un dépôt

On clique sur son portrait puis Your repositories. Puis New.

Voici un exemple de dépôt :

tuto_portainer_github_3.thumb.png.6d720fb37e6c31c93c9a45dc1f42e5b1.png

On clique sur Create repository.

On arrive sur notre dépôt, vide de tout fichier sauf du README qui contient la description entrée ci-avant.
On pourra évidemment en faire une documentation si on en éprouve le besoin.
On va maintenant changer le nom de la branche principale, pour qu'il corresponde à celui pré-établi par Portainer lors de la création d'une pile git. On clique sur branch :

tuto_portainer_github_4.thumb.png.1fbdd312b9ddc944de0d87a1c76523ae.png

Puis, on clique sur l'icône en forme de crayon tout à droite de la ligne de la branche "main". On la renomme en master et on valide.
On revient sur la page d'accueil du dépôt en cliquant sur Code.
On va maintenant pouvoir ajouter ou créer nos premiers fichiers.

7-C. Création & ajout d'un fichier compose

On a plusieurs possibilités :

  • On clique sur Add file (voir impression d'écran ci-avant) :
    • On télécharge sur le dépôt un fichier compose existant via Upload files.
    • On crée un nouveau fichier en cliquant sur Create new file.
  • On utilise git depuis sa machine pour alimenter son dépôt (non traîté).

On va ajouter un fichier docker-compose existant depuis mon PC.
On clique sur Upload files, on peut déposer le fichier à la volée ou parcourir l'arborescence, puis on clique sur Commit changes.
Une fois le fichier ajouté, il est à la racine du dépôt.
Si on souhaite créer une arborescence, il n'y a pas de fonction créer un dossier dans Github. Pour ce faire il faut éditer le fichier en question, en cliquant sur le fichier nouvellement ajouté puis la même icône crayon en haut à droite du cadre contenant le code du fichier.

tuto_portainer_github_5.thumb.png.0f6ca3c54bf34ddb9ba376b0bf35340c.png

Si vous regardez en haut de l'impression d'écran, j'ai créé un chemin synology/docker-compose_exemple.yml
Pour cela, j'ai simplement ajouté synology/ devant le nom du fichier compose, Github reconnaît une arborescence dès qu'on ajouté un /.

Je conseille également de renommer les fichiers docker-compose.yml en ajoutant l'application en question, ce sera nécessaire pour dissocier les fichiers les uns des autres.

Remarques :

  • Vous noterez que j'ai déposé le fichier compose de Portainer, c'est purement pour l'exemple. C'est évidemment le seul fichier compose qu'on ne pourra pas exploiter via Portainer. 😉 
  • Il n'y a pas d'adaptation spécifique à faire au niveau du contenu des fichiers compose par rapport à ce que vous avez l'habitude de faire directement via Portainer à la création d'une pile ou en ligne de commande.

Et maintenant direction Portainer !

7-D. Création de la pile

Dans Portainer, on sélectionne l'environnement où l'on souhaite déployer la pile, puis on clique sur Add stack.
On lui donne un nom, puis on choisit l'option Git repository.

On complète de la façon suivante :

tuto_portainer_github_7.thumb.png.f48df4ae291c8755e2e3801409c74f10.png

  • Repository URL : On y met l'adresse de notre dépôt, concaténé à partir du nom d'utilisateur Github et du nom du dépôt, à c/c depuis Github.
  • Repository reference : c'est la raison pour laquelle je vous ai fait changer le nom de la branche, Portainer propose master par défaut.
  • Compose path : le chemin du fichier compose sur le dépôt. Il est possible de le c/c directement depuis le fichier en question :

tuto_portainer_github_6.thumb.png.dca1fa026d143222b2cfe014b1948ab3.png

  • Authentication : Vu qu'on est sur un dépôt privé, il faut s'authentifer, c'est ici qu'on entre notre nom d'utilisateur Github ainsi que le PAT (Personal access token) généré précédemment.

tuto_portainer_github_8.thumb.png.2131548f1af36a8555ba330c0013b99c.png

  • Automatic updates : Gros intérêt de ce procédé, les conteneurs sont dynamiquement liés à leurs fichiers de configuration sur Github. Deux moyens de procéder :
    • Polling : Par défaut, toutes les 5 minutes, Portainer ira vérifier si le fichier a changé, si oui, il recréer le conteneur. Méthode la plus simple et recommandée
    • Webhook : C'est Github qui signalera à Portainer que le fichier a changé. Quand vous utilisez cette option, Portainer crée un lien vers son API, lien à renseigner dans Github. Cette méthode a plusieurs implications.

Il faut que les IP suivantes puissent accéder à votre instance Portainer (donc attention au pare-feu et au géo-blocage) :

- 192.30.252.0/22
- 185.199.108.0/22
- 140.82.112.0/20
- 143.55.64.0/20
- 2a0a:a440::/29
- 2606:50c0::/32

De plus, il faut aller renseigner le lien vers Portainer dans Github, pour cela on va sur notre dépôt :

tuto_portainer_github_9.thumb.png.ee9a1428bcb42dfc2b29c14234d4bfd8.png

On clique sur Add webhook puis :

tuto_portainer_github_10.thumb.png.4f8566d1fef31d388f1d6afe8e204ef9.png

On entre l'URL fournie par Portainer, pas besoin d'ajouter de secret. Si on utilise un proxy inversé ou le port sécurisé de Portainer, on laisse activée la vérification SSL.
Des tests que j'avais faits, seuls les commit comments ont besoin d'être cochés. Si ça ne fonctionne pas, choisissez Send me everything et éventuellement procédez par élimination pour trouver les événements nécessaires.

Je recommande la méthode du polling, pas de problème d'accès car la requête part de Portainer.

Il ne vous reste plus qu'à déployer les piles. 🙂 

7-E. Variables d'environnement

Il est possible, comme avec les outils natifs Portainer, de spécifier la valeur de variables d'environnement lors du déploiement de la pile.
Par exemple, dans le fichier compose :

environment:
  - MYSQL_ROOT_PASSWORD = ${MYSQL_ROOT_PASSWORD}

Au moment du déploiement :

tuto_portainer_github_11.thumb.png.a9011be9dacd4296b0d116aea0148708.png

Pour les férus de sécurité uniquement. 😉 

MàJ : 11/11/2022

Modifié par .Shad.
Modification remarques volumes Docker pour l'agent
Posté(e) (modifié)

Bravo pour ce guide / tuto !

J'ai d'une part découvert Portainer, que je ne connaissais que de nom mais n'avais jamais mis en œuvre, et d'autre part, presque le plus intéressant pour moi, la mise en place de la liaison TLS  avec création des certificats ... . Du coup je me concentre là dessus et essaie de comprendre en détail chaque étape. Ce n'est pas forcement trivial, cela demande de sérieuses lectures !

Merci en tout cas pour ce guide

Bruno78

Modifié par bruno78
  • 3 semaines après...
  • 2 mois après...
Posté(e)

Hello par ici 👋

@.Shad. 
Je viens de faire la première partie du tuto : installation de Portainer
image.thumb.png.28b2346704ca27cd2e24479d07fe1960.png image.png.c359a87b1faa0d690b8239ee0683b7e9.png

Pour le moment tout semble bien fonctionner.
J'ai juste légèrement modifié le fichier docker-compe.yml : (j'ai mis les PUID et GUID et ajouté le port 8000 qui semble nécessaire d'après la doc officielle)

version: "2.1"
services:
  portainer:
    image: portainer/portainer:latest
    container_name: portainer
    hostname: portainer
    network_mode: bridge
    environment:
      - PUID=1045
      - GUID=100
    volumes:
      - /volume1/docker/portainer/data:/data
      - /var/run/docker.sock:/var/run/docker.sock
    ports:
      - 8000:8000
      - 9000:9000
    restart: unless-stopped

 

Bref, jusque là pas de soucis.

Ma question porte en faire sur la sécurisation que tu fais ensuite.
En quoi ça sécurise le tout ? Ou bien dit autrement, en quoi la non réalisation engendre un manque de sécurité ?
Je précise que tout est sur le NAS, chez moi, dans le LAN. J'ai mis un mot de passe très solide pour le compte admin de Portainer.

Merci pour les réponses 😉 et bonne journée

 

Posté(e)
il y a 22 minutes, MilesTEG1 a dit :

(j'ai mis les PUID et GUID et ajouté le port 8000 qui semble nécessaire d'après la doc officielle)

Je veux bien la source car je n'ai rien trouvé qui semble laisser penser que Portainer ait mis en place un système de PUID/PGID (pour éviter de chmod-der les dossiers en fait).
J'ai trouvé ça qui dit que ça n'est pas prévu, mais ça date cela dit : https://github.com/portainer/portainer/issues/1535

Pour le port 8000, c'est pour ce qu'ils appellent le Edge Agent, ça ne nous concerne pas a priori (aux dires de leur doc), donc aucun besoin de NAT ce port sur le NAS.
On ne NAT que ce dont on a besoin.

Dans le cadre d'un réseau local, le besoin de sécurisation est moins prégnant car le postulat de départ est de supposer le réseau local comme sécurisé.
Reste qu'exposer le port 2375 (non sécurisé) pour un accès distant au socket Docker, c'est donner un accès potentiellement root à n'importe qui (ou quoi, malware compris) sur le Docker de la machine en question, et ça peut très vite se transformer en root sur la machine. Sans mot de passe on entend bien.

Donc ce serait l'équivalent de ne pas mettre d'utilisateur ni de mot de passe à DSM pour tous les périphériques de ton réseau local. Faut déjà un sacré niveau de confiance quand même.

L'idée ici est de mettre en place une liaison TLS client-serveur basée sur des certificats auto-signés, et surtout définir quelles IP et quels noms de domaines peuvent accéder à l'instance qu'on désire connecter.
Et ça évite surtout d'exposer nativement le socket aux requêtes externes.

En effet, quand DSM lance Docker, il n'est accessible qu'en boucle locale. Si on veut l'exposer sur un port, il y a un argument à ajouter tcp://0.0.0.0:2375 (ou quelque chose du genre) pour le rendre disponible au lancement du démon.
La modification de ce paramètre dans les tréfonds du SSH ne survit pas à la plupart des mises à jour systèmes, et risque surtout de faire planter ton NAS.

Ici, l'image de sjawhar fait office d'intermédiaire entre le démon exposé localement, et l'instance de Portainer qui essaie d'y accéder. Pour désactiver cet accès distant il suffit de désactiver ce conteneur, c'est complètement transparent pour le démon. Donc aucun redémarrage, et aucune incidence sinon que tu ne peux plus y accéder sans te connecter à la machine via UI ou SSH.

C'est pas forcément évident à comprendre, mais retient qu'il faut à tout prix éviter d'exposer le socket Docker autrement qu'en boucle locale, et que cette méthode permet tout de même un accès relativement sûr.
Si on veut pousser la chose plus loin, on peut mettre en place un VPN sur l'hôte du socket Docker distant, et n'autoriser que les IP du réseau VPN. C'est toi qui choisis où tu places le curseur de sécurité.

Posté(e)

@.Shad.

Ok pour le port 8000, j'ai recréé le conteneur sans 😉 (merci docker-compose car avec ça va plus vite ^^)

Sinon, pour le port 2375, il est d'office exposé ? Même si je ne l'ai pas spécifié dans le fichier docker-compose ??
Je comprend l'idée générale de ce que tu as dis ensuite, mais j'avoue que je n'ai pas tout saisi dans le détail...

Mon objectif est d'utiliser portainer uniquement depuis le réseau local. Le port 9000 ouvert permet de faire ceci. Mais comment le port 2375 peut-il être ouvert ?

Je comprends que s'il est bien ouvert et accessible, et que par ce biais, il n'y ait pas besoin de mot de passe pour accéder au NAS (en SSH ??), c'est effectivement un problème niveau sécurité... Mais je ne suis pas sur d'avoir compris si c'était bien le cas, que ce port soit ouvert...

 

Sinon pour les GUID et PUID, je n'ai de source... c'est de mon propre chef que je l'ai mis, comme je le fais pour tous les conteneurs, comme ça ils ne sont censés accéder qu'à ce que je leur autorise. Car je crée un utilisateur dédié pour chaque conteneur que je crée. C'est un peu fastidieux, mais bon 😉 j'ai un bon gestionnaire de mot de passe 🤪

 

Dernière chose, il y aurait une MAJ :

image.png.fb021122b53449a8d59978a4aab9bf4c.png

Un clic sur le message emmène ici : https://github.com/portainer/portainer/releases/tag/2.0.0
Mais dans docker hub, cette version 2 n'est pas présente.

 

Petite question : peux-tu expliquer rapidement ce que sont les templates dans portainer ?

 

En tout cas, merci pour le temps passé d'une part sur les tuto, et pour me répondre 👍

Posté(e)

Ok, merci pour ces explications sur la démarche de sécurisation 😉

Par contre, je n'ai toujours pas saisi si dans l'état actuel de mon NAS et de ce que j'ai fait avec Docker, si ce port 2376 est ouvert à toutes les IP LAN ... ?

Posté(e) (modifié)
Il y a 4 heures, MilesTEG1 a dit :

Sinon, pour le port 2375, il est d'office exposé ? Même si je ne l'ai pas spécifié dans le fichier docker-compose ??

On parle du socket Docker, c'est le fichier /var/run/docker.sock.
Quand DSM démarre Docker, ça revient à lancer la commande :

dockerd -H unix:///var/run/docker.sock

Ici on ne charge que le socket UNIX, il est donc inaccessible de l'extérieur. il est possible d'écrire aussi :

docker -H tcp://0.0.0.0:2375 ps

et dans ce cas-là on rend le socket Docker accessible à toutes les IP sur ce port (localement par défaut, ou à distance si on fait du NAT).

La méthode que je propose émule ceci :

dockerd --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem \
  -H=0.0.0.0:2376

On expose le socket Docker via TCP sur le port 2376, mais sur un port sécurisé qui nécessite d'avoir les certificats clients qui correspondent aux certificats serveurs qu'on charge au démarrage.

Plus d'infos :
https://stackoverflow.com/questions/35110146/
https://docs.docker.com/engine/reference/commandline/dockerd/
https://docs.docker.com/engine/security/https/

Mais vu qu'il est compliqué sur Synology de lancer Docker via des arguments que ceux prévus par DSM, on passe par ce conteneur qui permet d'exposer le socket local via le port 2376, sans pour autant toucher à la commande DSM, c'est transparent.

Il y a 4 heures, MilesTEG1 a dit :

Sinon pour les GUID et PUID, je n'ai de source... c'est de mon propre chef que je l'ai mis, comme je le fais pour tous les conteneurs, comme ça ils ne sont censés accéder qu'à ce que je leur autorise. Car je crée un utilisateur dédié pour chaque conteneur que je crée.

Attention, il faut que le créateur de l'image ait prévu de telles variables, ce n'est pas courant en réalité.
Le moyen de forcer l'emploi d'un utilisateur c'est :

environment:
...
user: "uid" # ou "uid:gid"
volumes:
...

En l'occurrence Portainer n'intègre pas ces variables d'environnement.

Il y a 4 heures, MilesTEG1 a dit :

Petite question : peux-tu expliquer rapidement ce que sont les templates dans portainer ?

Ça crée un conteneur directement depuis une image officielle.
Je n'ai jamais essayé de m'en servir car jamais eu besoin, je pense que c'est plus utile pour du développement.

Il y a 4 heures, MilesTEG1 a dit :

Dernière chose, il y aurait une MAJ :

image.png.fb021122b53449a8d59978a4aab9bf4c.png

Un clic sur le message emmène ici : https://github.com/portainer/portainer/releases/tag/2.0.0

Portainer-ce est principalement focus sur Kubernetes (K8s), qui est un concurrent de Docker qui est très utilisé dans les entreprises, ça n'a pas beaucoup de sens pour une utilisation en tant que particulier, sauf pour le côté ludique.

il y a 13 minutes, MilesTEG1 a dit :

Par contre, je n'ai toujours pas saisi si dans l'état actuel de mon NAS et de ce que j'ai fait avec Docker, si ce port 2376 est ouvert à toutes les IP LAN ... ?

Si tu as fait un NAT entre le NAS et le conteneur docker-socket-proxy dans ton fichier docker-compose, il est accessible sur ton NAS.
Pour qu'un périphérique autre y accède (par exemple l'hôte qui héberge Portainer), il faut que Portainer se connecte à IP_NAS:2376.

Modifié par .Shad.
Posté(e)
Il y a 13 heures, .Shad. a dit :

On parle du socket Docker, c'est le fichier /var/run/docker.sock.
Quand DSM démarre Docker, ça revient à lancer la commande :


dockerd -H unix:///var/run/docker.sock

Ici on ne charge que le socket UNIX, il est donc inaccessible de l'extérieur. il est possible d'écrire aussi :


docker -H tcp://0.0.0.0:2375 ps

et dans ce cas-là on rend le socket Docker accessible à toutes les IP sur ce port (localement par défaut, ou à distance si on fait du NAT).

La méthode que je propose émule ceci :


dockerd --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem \
  -H=0.0.0.0:2376

On expose le socket Docker via TCP sur le port 2376, mais sur un port sécurisé qui nécessite d'avoir les certificats clients qui correspondent aux certificats serveurs qu'on charge au démarrage.

Plus d'infos :
https://stackoverflow.com/questions/35110146/
https://docs.docker.com/engine/reference/commandline/dockerd/
https://docs.docker.com/engine/security/https/

Mais vu qu'il est compliqué sur Synology de lancer Docker via des arguments que ceux prévus par DSM, on passe par ce conteneur qui permet d'exposer le socket local via le port 2376, sans pour autant toucher à la commande DSM, c'est transparent.

Ok, comme dit précédemment, je n'ai pas taper ces commandes 😉 
Mais tu sembles dire que le fait d'avoir installer portainer et donc d'avoir mis en volume le socket ça fait la même chose que d'exposer via le port 2376, c'est bien ça ?

  

Il y a 13 heures, .Shad. a dit :

Attention, il faut que le créateur de l'image ait prévu de telles variables, ce n'est pas courant en réalité.
Le moyen de forcer l'emploi d'un utilisateur c'est :


environment:
...
user: "uid" # ou "uid:gid"
volumes:
...

En l'occurrence Portainer n'intègre pas ces variables d'environnement.

Ok, donc inutile de spécifier ces variables alors.
Mais du coup, si on ne spécifie pas l'utilisateur, le conteneur utilise quels droits d'accès sur les partages passés en volume ?

  

Il y a 13 heures, .Shad. a dit :

Ça crée un conteneur directement depuis une image officielle.
Je n'ai jamais essayé de m'en servir car jamais eu besoin, je pense que c'est plus utile pour du développement.

Ok, donc pas très utile pour moi aussi 😛

 

Il y a 13 heures, .Shad. a dit :

Portainer-ce est principalement focus sur Kubernetes (K8s), qui est un concurrent de Docker qui est très utilisé dans les entreprises, ça n'a pas beaucoup de sens pour une utilisation en tant que particulier, sauf pour le côté ludique.

Ok aussi, je reste donc sur la version actuellement installée 😉 
J'ai vraiment du mal avec ces nouveaux termes que je découvre... Kubernetes 🤨

 

Il y a 13 heures, .Shad. a dit :

Si tu as fait un NAT entre le NAS et le conteneur docker-socket-proxy dans ton fichier docker-compose, il est accessible sur ton NAS.
Pour qu'un périphérique autre y accède (par exemple l'hôte qui héberge Portainer), il faut que Portainer se connecte à IP_NAS:2376.

Ce que tu appelles faire un NAR entre le NAS et le conteneur, est-ce que c'est ça :
image.png.76b528f13212d630e05ff028c2ff1386.png

Car pour moi, le NAT c'est de faire translater un port de connexion : genre sur ma box, je NAT le port 1234 extérieur sur le port 2345 du LAN sur la machine XXXXX.

Et du coup, je ne comprends plus ta deuxième phrase. Mon hôte c'est le NAS non ? Portainer étant installé dans Docker qui est aussi sur le NAS.

Je suis vraiment désolé de mon incompétence à comprendre tout ça... Et je te remercie du temps que tu passes à essayer de m'expliquer le tout 😊

 

 

Posté(e)
Il y a 11 heures, MilesTEG1 a dit :

Mais tu sembles dire que le fait d'avoir installer portainer et donc d'avoir mis en volume le socket ça fait la même chose que d'exposer via le port 2376, c'est bien ça ?

Pas du tout.
Ce que je dis c'est que le conteneur issu de l'image sjawhar/docker-socket-proxy permet d'exposer le socket Docker d'une machine B sur un port TCP, sans que le démon n'ait été, au lancement de la machine B, configuré pour exposer autrement qu'en boucle locale (donc accessible uniquement depuis cette machine).
Et ce, de manière chiffrée par le biais d'un certificat.
Portainer facilite la connexion d'une instance Docker à une autre par une interface, mais on peut très bien le faire uniquement en lignes de commande.
Le fait d'avoir monter un dossier du NAS (/volume1/docker/portainer/data) dans le conteneur (/data) permet d'avoir une persistance des données, car même si le conteneur est supprimé, le contenu d'un dossier n'est pas supprimé. Si on ne met rien dans la partie volumes, alors Docker créera aussi des dossiers, dans dans un chemin invisible à l'utilisateur depuis DSM (/var/lib/docker/...), et qui s'effacera quand le conteneur disparaîtra.

Il y a 11 heures, MilesTEG1 a dit :

Mais du coup, si on ne spécifie pas l'utilisateur, le conteneur utilise quels droits d'accès sur les partages passés en volume ?

Quand rien n'est précisé au niveau de l'utilisateur, l'image crée les fichiers et dossiers avec le même UID/GID que celui utilisé dans le conteneur (c'est souvent là que ça rentre en conflit avec les NAS), et est exécutée par l’utilisateur root.

Il y a 11 heures, MilesTEG1 a dit :

J'ai vraiment du mal avec ces nouveaux termes que je découvre... Kubernetes 🤨

C'était pour la littérature que je l'évoquais, par rapport à ta question, un conseil laisse ça de côté pour le (un bon?) moment. 😉 

Il y a 11 heures, MilesTEG1 a dit :

Ce que tu appelles faire un NAR entre le NAS et le conteneur, est-ce que c'est ça :
image.png.76b528f13212d630e05ff028c2ff1386.png

Car pour moi, le NAT c'est de faire translater un port de connexion : genre sur ma box, je NAT le port 1234 extérieur sur le port 2345 du LAN sur la machine XXXXX.

Et du coup, je ne comprends plus ta deuxième phrase. Mon hôte c'est le NAS non ? Portainer étant installé dans Docker qui est aussi sur le NAS.

Non du tout, ce que tu fais là, mais je l'ai rappelé juste avant, c'est pour assurer une persistance des données, c'est tout.
Dans ton exemple de NAT, remplace la box par ton NAS, et ton NAS par le conteneur, et tu as exactement le même fonctionnement.
Quand tu écris :

ports:
  - 18096:8096

tu dis que le port 18096 de ton NAS doit rediriger vers le 8096 du conteneur.
Donc en tapant dans un navigateur : IP_du_NAS:18096, tu déboucheras sur ce qu'on trouve à IP_du_conteneur:8096.
Un conteneur est, par défaut, créé dans un sous-réseau privé, auquel seul le NAS, si les règles de pare-feu le permettent, peut y accéder.
Donc si tu essaies de t'y connecter depuis un PC sous Windows par exemple, c'est le NAS, à la manière d'une box, qui fait office de passerelle et rend les services que tu souhaites accessibles.

Posté(e) (modifié)

Hello @.Shad.

Le 17/09/2020 à 22:43, .Shad. a dit :
Le 17/09/2020 à 10:39, MilesTEG1 a dit :

J'ai vraiment du mal avec ces nouveaux termes que je découvre... Kubernetes 🤨

C'était pour la littérature que je l'évoquais, par rapport à ta question, un conseil laisse ça de côté pour le (un bon?) moment. 😉 

Ok, donc je laisse tomber cette MAJ et les Kubernetes 😁

 

Le 17/09/2020 à 22:43, .Shad. a dit :

Pas du tout.
Ce que je dis c'est que le conteneur issu de l'image sjawhar/docker-socket-proxy permet d'exposer le socket Docker d'une machine B sur un port TCP, sans que le démon n'ait été, au lancement de la machine B, configuré pour exposer autrement qu'en boucle locale (donc accessible uniquement depuis cette machine).
Et ce, de manière chiffrée par le biais d'un certificat.
Portainer facilite la connexion d'une instance Docker à une autre par une interface, mais on peut très bien le faire uniquement en lignes de commande.
Le fait d'avoir monter un dossier du NAS (/volume1/docker/portainer/data) dans le conteneur (/data) permet d'avoir une persistance des données, car même si le conteneur est supprimé, le contenu d'un dossier n'est pas supprimé. Si on ne met rien dans la partie volumes, alors Docker créera aussi des dossiers, dans dans un chemin invisible à l'utilisateur depuis DSM (/var/lib/docker/...), et qui s'effacera quand le conteneur disparaîtra.

Ok.
Je précise aussi je n'ai pas installé cette image là (sjawhar/docker-socket-proxy).

 

Le 17/09/2020 à 22:43, .Shad. a dit :

Quand rien n'est précisé au niveau de l'utilisateur, l'image crée les fichiers et dossiers avec le même UID/GID que celui utilisé dans le conteneur (c'est souvent là que ça rentre en conflit avec les NAS), et est exécutée par l’utilisateur root.

OK, je vois. Mais du coup si l'image n'a pas prévue l'utilisation du couple UID/GID, ça ne sert à rien de les spécifier, et il faut croiser les doigts pour pas que ça merde, c'est ça ?

Le 17/09/2020 à 22:43, .Shad. a dit :
Le 17/09/2020 à 10:39, MilesTEG1 a dit :

Ce que tu appelles faire un NAR entre le NAS et le conteneur, est-ce que c'est ça :
image.png.76b528f13212d630e05ff028c2ff1386.png

Car pour moi, le NAT c'est de faire translater un port de connexion : genre sur ma box, je NAT le port 1234 extérieur sur le port 2345 du LAN sur la machine XXXXX.

Et du coup, je ne comprends plus ta deuxième phrase. Mon hôte c'est le NAS non ? Portainer étant installé dans Docker qui est aussi sur le NAS.

Expand  

Non du tout, ce que tu fais là, mais je l'ai rappelé juste avant, c'est pour assurer une persistance des données, c'est tout.

J'aurais du copier juste la ligne /var/run/docker.sock car je parlais de ça ^^

Le 17/09/2020 à 22:43, .Shad. a dit :

Dans ton exemple de NAT, remplace la box par ton NAS, et ton NAS par le conteneur, et tu as exactement le même fonctionnement.
Quand tu écris :


ports:
  - 18096:8096

tu dis que le port 18096 de ton NAS doit rediriger vers le 8096 du conteneur.
Donc en tapant dans un navigateur : IP_du_NAS:18096, tu déboucheras sur ce qu'on trouve à IP_du_conteneur:8096.
Un conteneur est, par défaut, créé dans un sous-réseau privé, auquel seul le NAS, si les règles de pare-feu le permettent, peut y accéder.
Donc si tu essaies de t'y connecter depuis un PC sous Windows par exemple, c'est le NAS, à la manière d'une box, qui fait office de passerelle et rend les services que tu souhaites accessibles.

Ok, j'avais bien compris la chose, je n'avais cependant pas descendu d'un cran le raisonnement 😉

 

Donc grosso-modo, je n'ai pas de risque niveau sécurité actuellement, car si j'ai bien compris mon port 2376 du NAS n'est pas exposé.
J'ai bon ?

Modifié par MilesTEG1
Posté(e)
Le 20/09/2020 à 17:50, MilesTEG1 a dit :

OK, je vois. Mais du coup si l'image n'a pas prévue l'utilisation du couple UID/GID, ça ne sert à rien de les spécifier, et il faut croiser les doigts pour pas que ça merde, c'est ça ?

Ca ne va pas forcément coincer.
J'ai par exemple Baikal (serveur CardDAV/CalDAV) qui utilise l'uid/gid 33.
C'est un utilisateur réservé à DSM normalement, mais ça n'empêche pas de fonctionner, tant que tes volumes ont été chownés pour cet uid/gid.
Il faut juste regarder les droits que ça implique sur ces fichiers.

Le 20/09/2020 à 17:50, MilesTEG1 a dit :

J'aurais du copier juste la ligne /var/run/docker.sock car je parlais de ça ^^

Ca donne la main à Portainer sur le socket Docker local.

Le 20/09/2020 à 17:50, MilesTEG1 a dit :

Donc grosso-modo, je n'ai pas de risque niveau sécurité actuellement, car si j'ai bien compris mon port 2376 du NAS n'est pas exposé.

Il n'est utilisé nulle part, vu que tu n'utilises pas l'image de sjawhar et que Docker n'est accessible qu'en local (pas d'argument tcp au lancement).
Donc même si tu l'exposais, ça n'aurait aucun effet.

Posté(e)

Ceci n'est, il me semble, plus possible avec Portainer v2.

L'utilisation de l'Agent devient obligatoire. Ceux qui utilisent cette technique doivent rester en 1.24.1 !

  • 4 semaines après...
Posté(e)
Le 22/09/2020 à 00:00, EVOTk a dit :

Ceci n'est, il me semble, plus possible avec Portainer v2.

L'utilisation de l'Agent devient obligatoire. Ceux qui utilisent cette technique doivent rester en 1.24.1 !

C'est quoi cet Agent ?

Je viens d'installer la v2.0 de portainer (portainer-ce) avec ce docker-compose :

Citation

version: "2.1"
services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    hostname: portainer
    network_mode: bridge
    environment:
      - PUID=1045
      - GUID=100
    volumes:
      - /volume1/docker/portainer/data:/data
      - /var/run/docker.sock:/var/run/docker.sock
    ports:
      - 9000:9000
    restart: unless-stopped
 

Pas de soucis à la mise à jour et à la recréation du conteneur. Tout semble bien fonctionner.

 

Posté(e)
Il y a 9 heures, MilesTEG1 a dit :

C'est quoi cet Agent ?

Cela sert, a regrouper plusieurs instance docker sur une seule instance portainer

Comme ceci :

6LYPWFj.png

Posté(e)

Haaa ok 🙂 Je pensais bien qu'on pouvait regrouper diverses instance de docker sur un seul portainer.

Je suppose qu'il faut installer un agent sur chaque docker dont on veut placer dans l'instance Portainer qui doit tout afficher ?

Bon je n'ai qu'un seul NAS avec un seul Docker donc, je ne m'en servirais pas ^^

Posté(e)

Pour ceux que sa intéresse, voici pour installer l'agent  :

docker run -d -p 9001:9001 --name=portainer_agent --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/docker/volumes:/var/lib/docker/volumes portainer/agent

Puis sur Portainer, dans Endpoint > Add Endpoint

On rajout : Agent

On lui donne un nom ( Name )

et on lui donne l'adresse local + port ( Endpoint URL ) [ Ici, le port est 9001, donc si la machine qui a l'agent est sous l'ip 192.168.0.40, on indiquera 192.168.0.40:9001 ]

  • 3 mois après...
Posté(e)

@MilesTEG1

Bonjour,

Dis-moi cela vaut vraiment le coup d'utiliser Portainer ? Selon toi, qu'apporte-t-il finalement de plus dans la gestion quotidienne ?

Pour mes quelques conteneurs, je m'en sort assez bien avec mes docker-compose et quelques lignes de commandes SSH, mais pourquoi pas, j'hésite à franchir le pas. Ton avis STP ?

Cordialement

oracle7😉

Rejoindre la conversation

Vous pouvez publier maintenant et vous inscrire plus tard. Si vous avez un compte, connectez-vous maintenant pour publier avec votre compte.

Invité
Répondre à ce sujet…

×   Collé en tant que texte enrichi.   Coller en tant que texte brut à la place

  Seulement 75 émoticônes maximum sont autorisées.

×   Votre lien a été automatiquement intégré.   Afficher plutôt comme un lien

×   Votre contenu précédent a été rétabli.   Vider l’éditeur

×   Vous ne pouvez pas directement coller des images. Envoyez-les depuis votre ordinateur ou insérez-les depuis une URL.

×
×
  • Créer...

Information importante

Nous avons placé des cookies sur votre appareil pour aider à améliorer ce site. Vous pouvez choisir d’ajuster vos paramètres de cookie, sinon nous supposerons que vous êtes d’accord pour continuer.