Skip to content
Snippets Groups Projects
Commit d371507d authored by Mickael Masquelin's avatar Mickael Masquelin
Browse files

fix(main): Merge avec contenu branche security

parents 33f99394 0a0b2d55
No related branches found
No related tags found
No related merge requests found
...@@ -46,15 +46,258 @@ Tip : Utilisez la commande `docker container logs` sur le conteneur `mysql`afin ...@@ -46,15 +46,258 @@ Tip : Utilisez la commande `docker container logs` sur le conteneur `mysql`afin
## Exercice 02 : Registre ## Exercice 02 : Registre
### Travail à réaliser ### Détail de la configuration appliquée
Ci-dessous le détail de la stack `compose` mise en oeuvre :
```bash
# docker-compose.yml
services:
registry:
restart: always
image: registry:2
container_name: mon-registre
# On mappe le port 5000 exposé par défaut au port 5000 sur l'hôte Docker
ports:
- 5000:5000
environment:
# On déclare un fichier `htpasswd` qui contient des infos de connexion locales
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registre Formation Docker
volumes:
# On mappe des volumes locaux afin que les données soient persistantes
- ./data/:/var/lib/registry
- ./auth/:/auth
[...]
```
Le registre privé est situé derrière un reverse-proxy HTTPS (Traefik) qui écoute sur les ports 80/tcp et 443/tcp. Son adresse URL est `https://hub.chez-wam.info`. L'identifiant à utiliser est `formation`.
> Le mot de passe vous sera fourni sur les slides.
### Travail à faire
Nous allons créer d'abord un fichier `Dockerfile` dont le contenu sera le suivant :
```bash
FROM alpine:latest
RUN apk add --no-cache git
```
Une fois cette opération effectuée, construire une image nommée `alpine-git` et lui mettre un tag `v1.0.0`. En vue de le pousser dans notre registre privé, il faut au préalable créer un nouveau tag en respectant ce format : `<URL_REGISTRE>:<PORT_REGISTRE>/<NOM_STAGIAIRE>/<NOM_CT>`.
Exemple pour moi :
```bash
docker tag alpine-git:v1.0.0 hub.chez-wam.info:443/masqueli/alpine-git:v1.0.0
```
La commande `docker image list` vous retournera la liste des images par date de création (de la plus récente à la moins récente). Essayez de pousser votre image sur le registre. Exemple pour moi :
```bash
docker push hub.chez-wam.info:443/masqueli/alpine-git:v1.0.0
```
Votre client Docker doit refuser l'envoi et vous retourner un message du type "*no basic auth credentials*". C'est "normal" ! Il faut être authentifié pour envoyer sur le registre. Cela se fait à l'aide de la commande `docker login <URL_REGISTRE>:<PORT_REGISTRE>`. Vous communiquerez à votre client Docker l'identifiant (`formation`) et le mot de passe du registre associé.
```bash
docker login hub.chez-wam.info:443
```
Si tout se passe bien, le message "*Login Succeeded*" devrait apparaître. Essayez à nouveau de poussez l'image à l'aide de la commande `docker push`.
```bash
docker push hub.chez-wam.info:443/masqueli/alpine-git:v1.0.0
```
Nous allons créer ensuite un second fichier `Dockerfile` dont le contenu sera le suivant :
```bash
FROM alpine:latest
RUN apk add --no-cache mysql-client
ENTRYPOINT ["mysql"]
```
Nous construirons cette image qui sera nommée `alpine-mysql` avec un tag de version `v1.0.0` et nous la pousserons dans le registre comme vu précédemment dans cet exercice.
Vérifions maintenant le contenu du catalogue de notre registre. Nous pouvons utiliser la commande `curl` en CLI pour cela :
```bash
curl -u formation:<password> -X GET https://hub.chez-wam.info:443/v2/_catalog
```
Vous devriez obtenir la liste de vos images présentes dans le catalogue global (`<nom_stagiaire>/alpine-git` et `<nom_stagiaire>/alpine-mysql`). Nous allons ajouter le tag `latest` que nous avons oublié pour l'image `alpine-mysql` ... toujours en respectant le format décrit plus haut : `<URL_REGISTRE>:<PORT_REGISTRE>/<NOM_STAGIAIRE>/<NOM_CT>`. Une fois cette opération effectuée, envoyez l'image marquée `latest` dans le registre.
Vérifions maintenant les tags pour votre image `alpine-mysql`, toujours à l'aide de `curl` en CLI. Exemple pour moi :
```bash
curl -u formation:<password> -X GET https://hub.chez-wam.info:443/v2/masqueli/alpine-mysql/tags/list
```
Vous devriez obtenir, pour l'image `<nom_stagiaire>/alpine-mysql` les tags `v1.0.0` et `latest` au prompt. Nous allons enfin voir comment récupérer une image depuis le registre. Supprimez d'abord les images `<nom_stagiaire>/alpine-git:*` créées localement à l'aide de la commande `docker rmi`. Exemple pour moi :
```bash
# pour les clients Linux ou macOS
docker rmi $(docker images 'alpine-git' -a -q)
# pour les clients Windows, avec PowerShell
docker rmi $(docker images --format "{{.Repository}}:{{.Tag}}"|findstr "alpine-git")
```
Vérifiez à nouveau à l'aide de la commande `docker image list` que toutes les références et les images locales `alpine-git` ont bien été supprimées. Essayez maintenant de récupérer l'image depuis le registre. Exemple pour moi :
```bash
docker pull hub.chez-wam.info:443/masqueli/alpine-git:v1.0.0
```
Vérifiez à nouveau à l'aide de la commande `docker image list` qu'il existe bien une image correspondant au nom et au tag donné précédemment. Félicitations ! Vous connaissez désormais les opérateurs de base pour interagir avec un registre privé :)
### Construire une image à destination de plusieurs architectures
Vous pouvez, en une seule commande, lancer la construction d’images Docker à destination de plusieurs architectures (intel, arm, etc.). Pour rappel, **BuildKit** est le nouveau moteur de construction d’images intégrant pas mal de nouvelles fonctionnalités.
#### Mettre en place le builder
Le générateur est en fait une image, utilisant les fonctionnalités QEMU. Elle tourne au sein d’une instance Docker. C'est une fonctionnalité que l’on retrouve sur la version de Docker Desktop d’ailleurs ... mais elle est cachée.
Pour l'initialiser il faut, en premier lieu, lancer une image permettant d’installer l’outillage nécessaire sur la machine hôte. Cela se fait de la manière suivante :
```bash
$ docker run --privileged --rm tonistiigi/binfmt:latest --install all
```
Vous obtiendrez, en sortie, toutes les builds possibles. Ensuite, il faut créer le builder. Pour utiliser un registre personnel il faudra utiliser un fichier de config (en ajoutant `--config=/path/to/config.toml`). Je vous invite à consulter [ce lien](https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md) pour plus d'infos. Il faudra procéder de la manière suivante :
```bash
$ docker buildx create --driver-opt network=host --driver docker-container --name mybuilder --use mybuilder
```
// TODO Nous allons créer un `Dockerfile` très simple. Son contenu est le suivant :
```bash
# syntax=docker/dockerfile:1.3
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "Je suis exécuté sur $BUILDPLATFORM, je construis pour $TARGETPLATFORM" > /log
FROM alpine
COPY --from=build /log /log
```
Vous pouvez désormais lancer le build pour cette image de cette manière :
```bash
# Pour avoir une image adaptée aux plateformes Intel/AMD :
$ docker buildx build --platform linux/amd64 -t masqueli/test-amd64:v1.0.0 --load .
# Pour avoir une image adaptée aux plateformes ARM :
$ docker buildx build --platform linux/arm64 -t masqueli/test-arm64:v1.0.0 --load .
```
Félicitations ! Vous êtes en mesure de délivrer des images multi-plateformes désormais :)
## Exercice 03 : Réseau & Sécurité ## Exercice 03 : Réseau & Sécurité
### Travail à réaliser ### Utilisation de la directive USER
Clonez le projet disponible à l'adresse URL ci-après dans le répertoire de votre choix : [https://gitlab.univ-lille.fr/formation-docker-cnrs/session-debutant.git](https://gitlab.univ-lille.fr/formation-docker-cnrs/session-debutant.git).
Changez pour le répertoire `session-debutant` (ou autrement ... si vous l'avez renommé) puis créez le fichier `Dockerfile` ci-après :
```bash
FROM alpine:latest
RUN mkdir /app
RUN addgroup nemart && adduser -D -H -s /bin/false -G nemart jean
WORKDIR /app
COPY . /app
RUN chown -R jean:nemart /app
USER jean
CMD id && ls -alrt /app
```
Construisez cette image (que vous nommerez `exercice-user`) et exécutez là (`docker run --rm exercice-user`). Qu'observez-vous sur les droits (utilisateurs, fichiers, etc.) ?
> Attention : la suite peut être dangereuse si vous n'êtes pas bien reveillé ... ou en phase de digestion avancée :)
// TODO Nous allons être joueurs et monter le répertoire `/` de votre hôte (ou un autre si vous êtes sous Windows) afin de tester si nous sommes capables de supprimer des données lorsque nous sommes dans le conteneur. Ci-après la commande que j'utilise dans mon cas :
```bash
docker run -v /:/rep_serveur -it --rm exercice-user:latest sh
rm -f /rep_serveur/etc/passwd
```
Vous venez d'effacer le fichier `/etc/passwd` de votre machine (perso ou virtuelle) ... bravo !
... Non, je déconne, c'est une (mauvaise) blague. Si nous voulons que ça fonctionne, il faudra monter dans le conteneur un répertoire sur l'hôte qui possède les mêmes UID et GID que mon USER jean (1000:1000 par défaut) pour créer/modifier/supprimer des données.
Vous n'êtes pas protégé, malgré tout, d'une utilisation malveillante d'un utilisateur. Si vous lancez la commande précédente en utilisant l'option `-u <user>` lors du `run`, vous outre-passez cette règle (ce qui peut être dramatique pour le coup). Exemple pour moi :
```bash
docker run --rm -u root -it exercice-user:latest sh
id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
```
### Jouons avec les capabilities
#### Avec `cap-drop`
Le noyau Linux permet de segmenter les privilèges de l’utilisateur root en unités distinctes appelées capabilities. Par exemple, la capability `CAP_CHOWN` permet à un utilisateur d’apporter des modifications aux UID et aux GID des fichiers. Si je veux supprimer cette possibilité, j'utiliserai le couple paramètre/valeur suivant à l'appel de la commande `run` pour Docker en CLI : `--cap-drop=chown`.
Par défaut, Docker supprime toutes ces capabilities, à l’exception de celles qui lui sont nécessaires. Comment obtenir la liste des capabilities de votre container ??? Il suffit d’utiliser `getpcaps`. Ajoutons le à l'image créé dans l'exercice sur la directive USER. Ci-après le `Dockerfile` modifié :
```bash
FROM alpine:latest
RUN mkdir /app && \
addgroup nemart && \
adduser -D -H -s /bin/false -G nemart jean && \
apk --no-cache add libcap
WORKDIR /app
COPY . /app
RUN chown -R jean:nemart /app
USER jean
CMD getpcaps 1
```
Vérifiez, en lançant le conteneur, que les droits sont bien retirés. Faites un test au sein de votre conteneur.
#### Avec `seccomp`
Seccomp est une fonctionnalité du noyau Linux qui permet de filtrer les appels systèmes d’un processus. Pour cela il faut, bien sûr, que le noyau de votre serveur Linux ait été construit avec cette fonctionnalité activée. Pour contrôler cela sur un hôte Linux (à adapter pour Windows) :
```bash
grep SECCOMP= /boot/config-$(uname -r)
CONFIG_SECCOMP=y
CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
CONFIG_SECCOMP_FILTER=y
```
Docker utilise par défaut `seccomp`. Il est assez facile de vérifier cela :
```bash
docker run --rm -it exercice-user:latest sh
grep Seccomp /proc/$$/status
Seccomp: 2
```
Récupérons maintenant le profil utilisé par défaut pour Docker (le permalien : [https://gitlab.univ-lille.fr/formation-docker-cnrs/session-experimente/-/blob/7c86784ba6aa7b7860e3b777fba3481453c87908/docker-security/default.json](https://gitlab.univ-lille.fr/formation-docker-cnrs/session-experimente/-/blob/7c86784ba6aa7b7860e3b777fba3481453c87908/docker-security/default.json)) Il est disponible dans le dépôt GitLab. Nous allons le renommer pour l'appeler `mon-profil.json` et le modifier pour désactiver l'opération de `chmod`.
Nous changerons la valeur de la clef `defaultAction` pour la passer à `SCMP_ACT_ALLOW`. Puis, dans la branche `syscalls`, nous changerons la valeur de la clef `action` correspondant au nom `chmod`. La clef `action` recevra la nouvelle valeur `SCMP_ACT_ERRNO`.
Une fois ces modifications apportées, lançons le conteneur créé dans l'exercice sur la directive USER en utilisant le profil modifié. Exemple pour moi :
```bash
docker run --rm -it --security-opt seccomp:mon-profil.json exercice-user:latest sh
```
Essayez de faire un `chmod`. Qu'observez-vous ?
### Utilisation d'un scanner de vulnérabilités
Récupérez l'image de Trivy depuis le DockerHub (`aquasec/trivy`). Créez un répertoire local qui va recevoir la base de données des vulnérabilités de l'outil et montez celui-ci dans le répertoire `/root/.cache` d'un conteneur que vous appelerez `scanner`.
Effectuez maintenant un scan de toutes vulnérabilités de l'image `mysql:8.0` depuis le DockerHub (servez-vous de la slide qui présente des exemples d'utilisations de l'outil). Qu'observez-vous ? Faites à nouveau le test avec l'image `mysql:latest`. Quelle est votre conclusion ?
## Exercice 04 : Multi-Stage ## Exercice 04 : Multi-Stage
...@@ -75,7 +318,3 @@ Réaliser un Dockerfile Multi-Stage avec [Pandoc](https://pandoc.org/) pour cela ...@@ -75,7 +318,3 @@ Réaliser un Dockerfile Multi-Stage avec [Pandoc](https://pandoc.org/) pour cela
- [ ] Identifier votre image source depuis le [DockerHub](https://hub.docker.com) et l'initialiser si vous avez besoin de faire des installations dessus. - [ ] Identifier votre image source depuis le [DockerHub](https://hub.docker.com) et l'initialiser si vous avez besoin de faire des installations dessus.
- [ ] Copier sur votre image un fichier texte (docx, rtf, txt, md, ...) de votre choix et convertissez le en HTML. Jetez un oeil aux [démos](https://pandoc.org/demos.html) - [ ] Copier sur votre image un fichier texte (docx, rtf, txt, md, ...) de votre choix et convertissez le en HTML. Jetez un oeil aux [démos](https://pandoc.org/demos.html)
- [ ] Récupérer le fichier HTML ainsi générer et exposer le via une image NGINX - [ ] Récupérer le fichier HTML ainsi générer et exposer le via une image NGINX
## Exercice 05 : Méthode admin ?
This diff is collapsed.
...@@ -3,11 +3,9 @@ services: ...@@ -3,11 +3,9 @@ services:
image: mysql:8.0 image: mysql:8.0
container_name: db container_name: db
restart: unless-stopped restart: unless-stopped
env_file: .env
environment: environment:
- MYSQL_DATABASE=wordpress - MYSQL_DATABASE=wordpress
- MYSQL_ROOT_PASSWORD=mdprootmysql - MYSQL_ROOT_PASSWORD=mdprootmysql
- MYSQL_DATABASE=wordpress
- MYSQL_USER=wpuser - MYSQL_USER=wpuser
- MYSQL_PASSWORD=wppassword - MYSQL_PASSWORD=wppassword
volumes: volumes:
...@@ -22,7 +20,6 @@ services: ...@@ -22,7 +20,6 @@ services:
image: wordpress:latest image: wordpress:latest
container_name: wordpress container_name: wordpress
restart: unless-stopped restart: unless-stopped
env_file: .env
environment: environment:
- WORDPRESS_DB_HOST=db:3306 - WORDPRESS_DB_HOST=db:3306
- WORDPRESS_DB_USER=wpuser - WORDPRESS_DB_USER=wpuser
...@@ -34,3 +31,11 @@ services: ...@@ -34,3 +31,11 @@ services:
- 5555:80 - 5555:80
networks: networks:
- app-network - app-network
networks:
app-network:
driver: bridge
volumes:
dbdata:
wordpress:
...@@ -3,15 +3,19 @@ services: ...@@ -3,15 +3,19 @@ services:
restart: always restart: always
image: registry:2 image: registry:2
container_name: registre-prive container_name: registre-prive
# On mappe le port 5000 exposé par défaut au port 5000 sur l'hôte Docker
ports: ports:
- 5000:5000 - 5000:5000
environment: environment:
# On déclare les certificats serveurs à utiliser, connus des CA
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/moncertif.crt REGISTRY_HTTP_TLS_CERTIFICATE: /certs/moncertif.crt
REGISTRY_HTTP_TLS_KEY: /certs/macledecertif.key REGISTRY_HTTP_TLS_KEY: /certs/macledecertif.key
# On déclare un fichier `htpasswd` qui contient des infos de connexion locales
REGISTRY_AUTH: htpasswd REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
volumes: volumes:
# On utilise des volumes locaux afin que les données soient persistantes
- ./data/:/var/lib/registry - ./data/:/var/lib/registry
- ./certs/:/certs - ./certs/:/certs
- ./auth/:/auth - ./auth/:/auth
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment