Aller au contenu

Blog

Création :

Trivy scanne les vulns de conteneurs

C’est un fait les conteneurs sont de plus en plus utilisés, car ils facilitent la création et déploiements des applications. Mais attention, la sécurité des conteneurs est très importante, parce que vous pouvez rapidement créer de graves failles dans votre SI. En effet, par exemple récemment il a été détecté des conteneurs de la registry docker intégrant un ver de cryptojacking. Même si ces conteneurs ont été retirés, il est plus sage de soit construire ces images, soit de les scanner pour en connaître les vulnérabilités.

Monitoring d’ingress Traefik avec prometheus

logo prometheus

Dans la série monitoring, je vous propose de mettre en place le monitoring de l’ingress controller traefik préinstallé dans k3s. En fait sur k3s il est déjà actif et il ne reste plus qu’à installer le dashboard et à ajouter un job de scraping dans prometheus.

Activation du monitoring de traefik

Il suffit d’ajouter ces lignes au fichier de conf de Traefik.

[metrics]
[metrics.prometheus]
entryPoint = "traefik"

On peut vérifier que c’est le cas dans l’occurrence présente dans notre cluster k3s.

Terminal window
kubectl get configmaps traefik -o yaml -n kube-system

Vous devriez obtenir les lignes suivantes :

apiVersion: v1
data:
traefik.toml: |
logLevel = "info"
defaultEntryPoints = ["http","https"]
[entryPoints]
[entryPoints.http]
address = ":80"
compress = true
[entryPoints.https]
address = ":443"
compress = true
[entryPoints.https.tls]
[[entryPoints.https.tls.certificates]]
CertFile = "/ssl/tls.crt"
KeyFile = "/ssl/tls.key"
[ping]
entryPoint = "http"
[kubernetes]
[kubernetes.ingressEndpoint]
publishedService = "kube-system/traefik"
[accessLog]
format="json"
bufferingSize = 100
[traefikLog]
format = "json"
[metrics]
[metrics.prometheus]
entryPoint = "traefik"
kind: ConfigMap
metadata:
creationTimestamp: "2019-09-23T10:35:40Z"
labels:
app: traefik
chart: traefik-1.77.1
heritage: Tiller
release: traefik
name: traefik

Ajout d’un scraper dans prometheus

Je vous renvoie à l’installation de prometheus si ce n’est pas encore fait. D’ailleurs nous allons simplement modifier le fichier my-values et régénérer les manifests.

Éditer le fichier my-values.yaml et à la ligne 1046 ajouter les lignes suivantes comme ci-dessous :

1046 scrape_configs:
1047 - job_name: traefik
1048 static_configs:
1049 - targets:
1050 - traefik.kube-system.svc.cluster.local:8080
1051 - job_name: prometheus
1052 static_configs:
1053 - targets:
1054 - localhost:9090

Maintenant, il faut regénérer les manifests, faire un apply et de relancer prometheus :

Terminal window
cd manifests/prometheus/templates/
rm -rf manifests && mkdir manifests
helm template -f my-values.yaml --output-dir ./manifests --name prometheus --namespace monitoring .
cd manifests/prometheus/templates/
kubectl apply -f .
kubectl --n monitoring rollout restart deployment prometheus-server

Installation du dashboard traefik

Le dashboard Traefik est disponible à l’adresse suivante. Télécharger le json et importer le.

traefik dashboard grafana

Il faudra certainement également installer le plugin pie chart de grafana avant. On doit se connecter au pod et lancer la commande suivante (remplacer le nom du pod):

Terminal window
kubectl exec -it grafana-7f88c894b6-fmwm6 /bin/bash
grafana-cli plugins install grafana-piechart-panel

Quitter le pod et relancer le déploiement :

Terminal window
kubectl rollout restart deployment grafana

Au bout de quelques minutes retournez dans grafana et vous devriez avoir votre dashboard complet !

Vue de plusieurs Logs Kubernetes avec Stern

A l’image de multitail qui permet d’afficher un ou plusieurs fichiers, stern va vous permettre de concaténer les logs de plusieurs containers en un seul flux.

Découverte de Stern

Stern

  • Pour permettre de déboguer rapidement stern va afficher les logs dans un code couleur.
  • Stern va prendre en argument un simple paramètre qui peut-être un mot-clé commun de vos pods. Par exemple si vous avez les pods suivants :
Terminal window
drone-drone-server-6569df48f8-s4vf5 1/1 Running 1 8d
nfs-client-provisioner-865d85667d-zpw2z 1/1 Running 1 8d
gogs-88d668695-696s7 1/1 Running 1 8d
local-docker-registry-7fdbcc5648-6j6sv 1/1 Running 1 8d
blog-deployment-766bf75767-v9654 1/1 Running 0 11h
blog-deployment-766bf75767-x9p7w 1/1 Running 0 11h
blog-deployment-766bf75767-6rsqd 1/1 Running 0 11h

Et bien en tapant la commande suivante :

Terminal window
stern blog

Vous obtiendrez les logs de tous les pods contenant le mot blog. De même si un pod vient à mourir et à être remplacé par un nouveau, stern ajoute automatiquement ce nouveau flux de logs.

Options importantes

OptionDescription
—exclude-container <nom>Permet d’exclude des containers d’un pod contenant des sidecars
—container-state <status>Permet de filter en fonction de l’état du pod : running, waiting or terminated. Par défaut running.
—since <temps>N’affiche que les dernières logs depuis le temps indiqué : 52, 2m, or 3h.
—exclude <chaine>Permet d’excludre certaines lignes. Vous pouvez en ajouter plusieurs.
—namespace <nom>Change de namespace
—kubeconfig <fichier-config>Le fichier de config à prendre en compte
—all-namespacesAffiche les logs de tous les namespaces
—tail nN’affiche que les n lignes
—color <status>Force le jeu de couleur ou pas. auto, always, never
—output <type>Format de sortie : default, raw, json

Installation de stern

Terminal window
wget https://github.com/wercker/stern/releases/download/1.11.0/stern_linux_amd64 -o stern
sudo install stern /usr/local/bin

Plus d’infos sur le repo de stern

Tester vos API swagger/openAPI avec Dredd

Logo Dredd

Après vous avoir présenté une méthode utilisant postman/newman je suis parti d’une autre solution et je suis tombé sur dredd qui est nettement plus simple à mettre en oeuvre. En effet il fonctionne sans aucune création de fichier puisqu’il se base sur le fichier de déclaration de l’API.

Installation de Dredd

Dredd est basé sur Node.js et donc avant de l’installer il faut installer nodejs. Je vais vous montrer au passage une méthode d’installation des packages nodejs sans avoir besoin de recourir à sudo.

Terminal window
apt install nodejs npm
mkdir "${HOME}/.npm-packages"
npm config set prefix "${HOME}/.npm-packages"

Ajouter les lignes suivantes à votre fichier .bahsrc

Terminal window
NPM_PACKAGES="${HOME}/.npm-packages"
export PATH="$PATH:$NPM_PACKAGES/bin"

Recharger votre environnement et lancer les commandes suivantes :

Terminal window
npm install -g npm
dredd --help

Lancer ses premiers tests avec Dredd

Dredd possède une série d’option mais personnellement je préfère utiliser le fichier de configuration. Pour créer ce fichier de configuration il suffit de lancer la commande dredd init et de répondre aux questions. Mais avant on va récupérer le créer le fichier swagger permettant de tester le petstore.

Terminal window
vi swagger.yaml

Collez ceci dedans :

openapi: 3.0.1
info:
title: Swagger Petstore
version: 1.0.0
servers:
- url: https://petstore.swagger.io/v2
paths:
/pet/{petId}:
get:
tags:
- "pet"
summary: "Find pet by ID"
description: "Returns a single pet"
operationId: "getPetById"
produces:
- "application/xml"
- "application/json"
parameters:
- name: "petId"
in: "path"
description: "ID of pet to return"
required: true
type: "integer"
format: "int64"
responses:
200:
description: "successful operation"
schema:
$ref: "#/definitions/Pet"
components:
schemas:
Category:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: Category
Tag:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: Tag
Pet:
required:
- name
- photoUrls
type: object
properties:
id:
type: integer
format: int64
category:
$ref: ’#/components/schemas/Category’
name:
type: string
example: doggie
photoUrls:
type: array
xml:
name: photoUrl
wrapped: true
items:
type: string
tags:
type: array
xml:
name: tag
wrapped: true
items:
$ref: ’#/components/schemas/Tag’
status:
type: string
description: pet status in the store
enum:
- available
- pending
- sold
xml:
name: Pet

Maintenant générons le fichier de configuration de Dredd.

Terminal window
dredd init
? Location of the API description document swagger.yaml
? Command to start the API server under
? Host of the API under https://petstore.swagger.io/v2/
? Do you want to use hooks to customize Dredd’s behavior? No
? Do you want to report your tests to the Apiary inspector? No
? Dredd is best served with Continuous Integration. Do you want to create CI configuration? No

Le fichier de config est crée et se nomme dredd.yml vous pouvez modifier le contenu comme ceci :

color: true
dry-run: null
hookfiles: null
language: null
require: null
server: nom
server-wait: 3
init: false
custom: {}
names: false
only: []
reporter: []
output: []
header: []
sorted: false
user: null
inline-errors: false
details: false
method: []
loglevel: error
path: []
hooks-worker-timeout: 5000
hooks-worker-connect-timeout: 1500
hooks-worker-connect-retry: 500
hooks-worker-after-connect-wait: 100
hooks-worker-term-timeout: 5000
hooks-worker-term-retry: 500
hooks-worker-handler-host: 127.0.0.1
hooks-worker-handler-port: 61321
config: ./dredd.yml
blueprint: ./swagger.yaml
endpoint: ’https://petstore.swagger.io/v2’

Maintenant on peut lancer le test de l’API :

Terminal window
dredd
pass: GET (200) /pet/123 duration: 1211ms
request:
method: GET
uri: /pet/123
headers:
User-Agent: Dredd/12.1.0 (Linux 5.3.0-18-generic; x64)
body:
expected:
headers:
statusCode: 200
actual:
statusCode: 200
headers:
date: Wed, 16 Oct 2019 17:33:18 GMT
access-control-allow-origin: *
access-control-allow-methods: GET, POST, DELETE, PUT
access-control-allow-headers: Content-Type, api_key, Authorization
content-type: application/json
connection: close
server: Jetty(9.2.9.v20150224)
bodyEncoding: utf-8
body:
{
"id": 123,
"name": "doggie",
"photoUrls": [],
"tags": [],
"status": "available"
}
complete: 1 passing, 0 failing, 0 errors, 0 skipped, 1 total
complete: Tests took 1218ms

Et voila notre test est terminé. Vous pouvez aller plus loin en lisant la documentation du site

Tester vos API avec Postman/Newman

Pouvoir tester ces endpoint d’API fait parti des impératifs avant tout déploiement d’une nouvelle version pour voir si une régression entre autre n’est pas survenue avec cette mise jour.

A ce jour il existe peu d’outils permettant de le faire assez rapidement. L’idée est de pouvoir à partir du fichier de description de votre API générer en quelques clics voir automatiquement les tests. Ce traitement pourra bien sur être intégré dans votre CI via une image docker. Nous verrons aujourd’hui la solution Postman et de son compagnon Newman.

Installation de Postman sur Ubuntu

Installation via snap

Comme souvent, Postman est disponible en package snap.

Terminal window
sudo snap install postman --classic

Installation depuis le site

Si vous souhaitez installer une version plus récente :

Terminal window
apt install libgconf-2-4 postman
cd /tmp
wget https://dl.pstmn.io/download/latest/linux64 -O postman.tar.gz
sudo tar -xzf postman.tar.gz -C /opt
sudo ln -s /opt/Postman/Postman /usr/bin/postman
cd
postman

Création d’un lanceur (optionnel)

Terminal window
cat > ~/.local/share/applications/postman.desktop <<EOF
[Desktop Entry]
Encoding=UTF-8
Name=Postman
Exec=postman
Icon=/opt/Postman/app/resources/app/assets/icon.webp
Terminal=false
Type=Application
Categories=Development;
EOF

Prise en main

Faisons un rapide tour du propriétaire :

L’écran principal de Postman

L’écran de Postman se décompose en plusieurs parties dont voici les plus importantes :

  1. Ici on retrouve les collections qui permet de classer vos requêtes de manière fonctionnelle.
  2. Ici on a sous forme d’onglets les différentes requêtes ouvertes.
  3. Une liste déroulante permettant de choisir le type de requête à envoyer : GET, POST, PUT et à coté l’URL de l’API
  4. Les paramètres à passer à l’URL.
  5. La réponse à la requête
  6. Ici vous trouverez les paramètres de l’environnement.

Importation d’un fichier de définition d’une API

En cliquant sur le menu [import] il est possible de convertir un fichier de définition d’API (swagger, openAPI) en une collection postman

Prenons comme exemple le petstore au format OpenAPI 2. Il est possible de charger des fichiers au format v3

Terminal window
wget https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore.yaml

Création d’un environnement

En cliquant sur la roue dentelé dans la zone 6 il est possible de créer des variables d’environnement, ici baseUrl.

L’écran principal de Postman

  1. Cliquer sur Add et donner un nom à votre environnement
  2. Cliquer dans la première case de la première ligne et taper baseUrl
  3. Dans la case suivante entrer ceci : https://petstore.swagger.io/v2
  4. Cliquer sur Add et fermer cette fenêtre
  5. Dans la liste déroulante environnement choisir prod.

Envoi de notre première requête

  1. Cliquer dans la collection petstore petid POST Find pet by ID
  2. Dans la partie params / Path variable entrer 112273
  3. Cliquer sur send et vous devriez recevoir cette réponse :
{
"id": 112273,
"category": {
"id": 0,
"name": "string"
},
"name": "doggie",
"photoUrls": [
"string"
],
"tags": [
{
"id": 0,
"name": "string"
}
],
"status": "available"
}

Écriture du premier test

Il suffit de cliquer sur la partie Test au bout de la ligne des params :

A coté de l’éditeur se trouve une zone de raccourci (snippets) permettant de créer rapidement des tests

L’écran principal de Postman

Cliquer sur status Code Code is 200

Vous devriez voir apparaître ces lignes :

pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});

Ensuite cliquer sur Response headers Content-Type header check

Vous pouvez tester à tout moment tester en cliquant sur send. Vous devriez obtenir cet affichage :

L’écran principal de Postman

Automatisation des tests avec Newman

Il est possible d’automatiser les tests avec un outil fourni par postman. Dans un premier temps il faut exporter sa collection sous forme de fichier json. Il faut cliquer sur les … de votre collection et choisir Export. Garder le format proposé et appelez le petstore-collection.json. Avant n’oubliez pas de sauvegarder les modifications de la requête en cliquant sur Save en haut à droite de l’écran.

L’écran principal de Postman

Plutôt que d’installer Newman en local nous allons l’utiliser sous forme d’image docker:

Terminal window
docker run -v ~/collections:/etc/newman -t postman/newman_ubuntu run "petstore-collection.json"

Vous devriez obtenir ceci :

newman
Swagger Petstore
❏ pet / {pet Id}
↳ Find pet by ID
GET https://petstore.swagger.io/v2/pet/112273 [200 OK, 436B, 1089ms]
✓ Status code is 200
✓ Content-Type is present
1. Response time is less than 200ms
↳ Updates a pet in the store with form data
POST https://petstore.swagger.io/v2/pet/<long> [404 Not Found, 403B, 553ms]
...
┌─────────────────────────┬────────────────────┬────────────────────┐
│ │ executed │ failed │
├─────────────────────────┼────────────────────┼────────────────────┤
│ iterations │ 1 │ 0 │
├─────────────────────────┼────────────────────┼────────────────────┤
│ requests │ 20 │ 0 │
├─────────────────────────┼────────────────────┼────────────────────┤
│ test-scripts │ 1 │ 0 │
├─────────────────────────┼────────────────────┼────────────────────┤
│ prerequest-scripts │ 0 │ 0 │
├─────────────────────────┼────────────────────┼────────────────────┤
│ assertions │ 3 │ 1 │
├─────────────────────────┴────────────────────┴────────────────────┤
│ total run duration: 10.7s │
├───────────────────────────────────────────────────────────────────┤
│ total data received: 2.59MB (approx) │
├───────────────────────────────────────────────────────────────────┤
│ average response time: 492ms [min: 316ms, max: 2.2s, s.d.: 422ms] │
└───────────────────────────────────────────────────────────────────┘
# failure detail
1. AssertionError Response time is less than 200ms
expected 1089 to be below 200
at assertion:2 in test-script
inside "pet / {pet Id} / Find pet by ID"
bob@bob-HP:~/Projets/blog$

Dans mon cas le résultat de mon test a partiellement fonctionné avec une assertion négative !!!

Il ne reste plus qu’a écrire vos tests et à intégrer cette étape dan votre CI avant déploiement en prod. De même New peut parfaitement servir à tester votre API avant de le pousser dans votre repo de code.

Protéger les sites k3s avec Let’s Encrypt

Le protocole HTTP, simple et efficace, a cependant un énorme défaut : toutes les informations envoyées sont “en clair”. Donc cela signifie qu’elles peuvent être interceptées et modifiées par des personnes plus ou moins bien intentionnées (pirates, gouvernements, fournisseurs d’accès…)

Installer Ansible sur Windows avec CygWin

logo ansible

Au départ, je me suis dit d’installer Virtualbox et de monter une VM minimale mais sur mon poste du boulot ça le fait pas. Il me fallait une solution donc minimaliste. Par défaut au boulot Cywgin est installé et après lecture de ce billet je me suis lancé et à vrai dire tout fonctionne correctement en ajoutant quelques paramètres dans la config d’Ansible.

Installation d’Ansible cygwin

Ansible cygwin - Ansible windows

Télécharger et installer CygWin en utilisant la commande ci-dessous (changer le nom du setup s’il faut) :

Terminal window
setup-x86_64.exe -q --packages=binutils,curl,cygwin32-gcc-g++,gcc-g++,wget

Ouvrez une fenêtre Cygwin en mode administrateur et tapez les commandes suivantes pour installer Ansible (ça va être long) :

Terminal window
wget https://raw.githubusercontent.com/transcode-open/apt-cyg/master/apt-cyg
install apt-cyg /bin
apt-cyg install git gmp libffi-devel libgmp-devel make nano openssh openssl-devel python3 python3-devel libssl-devel python39-cryptography
pip3 install ansible --user
echo 'export PATH=$PATH:~/.local/bin'/>>~/.bash_profile

Premiers tests d’ansible Cywin

Dans un autre terminal cygwin (lancement normal)

Terminal window
ansible --version

qui devrait vous retourner :

Terminal window
ansible [core 2.12.3]
config file = None
configured module search path = ['/home/stephane.r/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/stephane.r/.local/lib/python3.9/site-packages/ansible
ansible collection location = /home/stephane.r/.ansible/collections:/usr/share/ansible/collections
executable location = /home/stephane.r/.local/bin/ansible
python version = 3.9.10 (main, Jan 20 2022, 21:37:52) [GCC 11.2.0]
jinja version = 3.0.3
libyaml = False

Vérifions avec un simple appel au module setup d’Ansible.

Terminal window
ansible -m setup localhost

qui devrait vous retourner :

Terminal window
[WARNING]: No inventory was parsed, only implicit localhost is available
localhost | SUCCESS => {
"ansible_facts": {
"ansible_apparmor": {
"status": "disabled"
},
"ansible_architecture": "x86_64",
"ansible_date_time": {
"date": "2021-03-03",
"day": "03",
"epoch": "1614772664",

Si vous avez d’autres produits à installer ne pas oublier de le faire dans un terminal démarrer en mode admin.

Pour la suite sur Ansible, je vous renvoie sur la lecture de mes autres billets :