Skip to main content

In this article

Sécurisez le développement de vos applications Kubernetes avec Snyk et Tilt

Écrit par

11 janvier 2021

0 minutes de lecture

Développer des applications Kubernetes peut s’avérer complexe. Nous devons souvent gérer des architectures de microservices comportant de nombreux éléments, ainsi que développer la configuration du cluster pour les connecter. Les workflows permettant d’itérer et de tester rapidement peuvent alors devenir complexes et difficiles à gérer pour les équipes d’ingénierie.

C’est là que des outils comme Tilt entrent en jeu.

Qu’est-ce que Tilt ?

Tilt est un outil formidable pour les développeurs qui travaillent sur des applications Kubernetes. Il propose un workflow de déploiement local hautement configurable qui se met à jour en temps réel pendant que vous travaillez. Au lieu de gérer un ensemble centralisé d’environnements dans lesquels tous vos développeurs travaillent, Tilt fournit à chacun son propre environnement local, avec des déploiements ultrarapides, et lui permet de personnaliser son workflow.

Tilt se configure à l’aide d’un fichier Tilftfile basé sur Starlark. Il propose une interface graphique et une CLI personnalisables, ainsi qu’une API complète. Vous disposez ainsi d’une grande flexibilité pour définir vos workflows et les informations à afficher pendant le développement de votre application. Tilt prend également en charge les extensions personnalisées, ce qui vous offre encore plus d’options pour configurer et exécuter votre environnement de développement.

Mon collègue Shai Mendel a contribué à une extension Tilt qui permet d’exécuter Snyk dans votre Tiltfile. J’ai récemment consacré du temps à mettre à jour cette extension et à explorer les différentes façons d’intégrer Snyk à vos workflows Tilt. Voyons comment cela fonctionne.

Pour commencer

La première étape consiste à déployer un cluster Kubernetes local, ainsi qu’un registre. Tilt fournit un outil qui simplifie grandement cette opération. Ctlptl permet d’exécuter Kubernetes en local de différentes façons, notamment avec miniKube, Kind et Docker Desktop. J’utilise Kind, que vous devez d’abord installer.

matt@Mattbook kind % brew install tilt-dev/tap/ctlptl
matt@Mattbook kind % ctlptl create cluster kind --registry=ctlptl-registry

Cette commande déploie un cluster Kubernetes avec Kind, ainsi qu’un registre dans votre instance Docker locale, et configure tout ce dont Kubernetes a besoin pour l’utiliser. Une fois la commande exécutée, nous pouvons voir dans Docker que les deux éléments ont été déployés :

matt@Mattbook 2-optimized-dockerfile % docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED         STATUS         PORTS                       NAMES
2f38ce48a6c9   kindest/node:v1.19.1   "/usr/local/bin/entr…"   2 minutes ago   Up 2 minutes   127.0.0.1:61091->6443/tcp   kind-control-plane
e61df5496e19   registry:2             "/entrypoint.sh /etc…"   3 minutes ago   Up 3 minutes   0.0.0.0:60923->5000/tcp     ctlptl-registry

Ensuite, je vais cloner une application exemple pour la tester. J’utilise l’exemple Node.js fourni par Tilt. Cette application comprend plusieurs Tiltfiles d’exemple qui présentent les bonnes pratiques d’utilisation de Tilt, ainsi qu’un article de blog qui les explique en détail.

Pour cet article, nous allons utiliser l’exemple de Dockerfile optimisé. Regardons le Tiltfile fourni :

matt@Mattbook 2-optimized-dockerfile % cat Tiltfile 
# -*- mode: Python -*

k8s_yaml('kubernetes.yaml')
k8s_resource('example-nodejs', port_forwards=8000,
    resource_deps=['deploy']
)

# Records the current time, then kicks off a server update.
# Normally, you would let Tilt do deploys automatically, but this
# shows you how to set up a custom workflow that measures it.
local_resource(
    'deploy',
    'python now.py > start-time.txt',
)

docker_build('example-nodejs-image', '.')

En examinant ce que fait ce Tiltfile, on voit qu’il utilise la fonction docker_build pour créer l’image example-node-js-image à partir du Dockerfile situé dans ce dossier. Il crée également une ressource k8s_yaml à l’aide du fichier kubernetes.yaml, puis l’associe à une ressource k8s_resource avec une redirection de port vers le port 8000. Une ressource local_resource est aussi définie : elle écrit l’heure actuelle dans un fichier et crée une dépendance entre ce fichier et k8s_resource. Elle s’exécute donc toujours avant la mise à jour de la ressource et permet de mesurer le temps de déploiement. Consultez la documentation pour en savoir plus sur toutes les fonctions prises en charge par l’API Tiltfile.

Nous avons également un Dockerfile qui installe les dépendances nécessaires à notre application avec yarn :

matt@Mattbook 2-optimized-dockerfile % cat Dockerfile 
FROM node:10

WORKDIR /app

ADD package.json .
ADD yarn.lock .
RUN yarn install

ADD . .

ENTRYPOINT [ "node", "/app/index.js" ]

Notre application est elle-même très simple : cette application Node.js se contente d’afficher une page Web sur le port 8000 :

matt@Mattbook 2-optimized-dockerfile % cat index.js 
/*
 Gratefully adapted from https://github.com/HemingwayLee/sample-mustache-express
*/const fs = require('fs');

const express = require('express');
const app = express();
const mustacheExpress = require('mustache-express');

let timeSince = 'N/A';

app.engine('mustache', mustacheExpress());

app.set('view engine', 'mustache');
app.set('views', __dirname + '/views');
app.use(express.static('public'));

app.get('/', (req, res) => {
    res.render('index.mustache', {
        time: timeSince,
    });
});

app.listen(8000, () => {
    timeSince = getSecsSinceDeploy();
    console.log('Server running at http://localhost:8000/');
});

function getSecsSinceDeploy() {
    let curTimeMs = new Date().getTime();
    let contents = fs.readFileSync('/app/start-time.txt', 'utf8');
    let startTimeMs  = parseInt(contents.trim()) / 10**6;
    return ((curTimeMs - startTimeMs) / 10**3).toFixed(2)

Maintenant que notre cluster est déployé, nous pouvons démarrer Tilt. Depuis le dossier qui contient notre Tiltfile, il suffit d’exécuter :

matt@Mattbook 2-optimized-dockerfile % tilt up
Tilt started on http://localhost:10350/
v0.17.13, built 2020-12-02

(space) to open the browser
(s) to stream logs (--stream=true)
(t) to open legacy terminal mode (--legacy=true)
(ctrl-c) to exit

Lorsque Tilt s’exécute dans le terminal, appuyez sur la barre d’espace pour ouvrir son interface dans votre navigateur.

Journal de déploiement affiché dans un navigateur sombre, présentant l’injection de YAML Kubernetes, le suivi du déploiement progressif et des panneaux d’état des services

Tilt exécute le Tiltfile et lance toutes les ressources définies. Dans notre cas, il crée l’image, la déploie sur Kubernetes et configure une redirection de port pour accéder à l’application.

Avec kubectl, nous pouvons vérifier que le pod exécutant l’application est bien en cours d’exécution :

matt@Mattbook snyk % kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
example-nodejs-7577d84888-d6q5t   1/1     Running   0          3m6s

En accédant à http://localhost:8000, nous pouvons voir l’application :

Page de navigateur affichant trois chats dessinés sur un éclair, au-dessus du texte « Hello cats! » et d’informations sur la durée du déploiement.

Une fois Tilt lancé, toute modification apportée aux répertoires et aux fichiers surveillés déclenche une nouvelle exécution du Tiltfile, puis la création et le redéploiement automatiques de l’application.

Voyons maintenant comment intégrer Snyk à ce workflow. Si ce n’est pas déjà fait, vous devez installer la CLI Snyk et créer un compte Snyk gratuit.

La première étape consiste à charger l’extension Snyk dans le Tiltfile :

# -*- mode: Python -*

load('ext://snyk', 'snyk')

k8s_yaml('kubernetes.yaml')
k8s_resource('example-nodejs', port_forwards=8000,
    resource_deps=['deploy']
)

# Records the current time, then kicks off a server update.
# Normally, you would let Tilt do deploys automatically, but this
# shows you how to set up a custom workflow that measures it.
local_resource(
    'deploy',
    'python now.py > start-time.txt',
)

docker_build('example-nodejs-image', '.')

À cette étape, Tilt exécute automatiquement le Tiltfile à nouveau. Dans le dossier de notre application, un répertoire tilt_modules apparaît. Il contient l’extension Snyk. Les extensions sont elles-mêmes des Tiltfiles, qui sont chargés dans le Tiltfile principal.

matt@Mattbook 2-optimized-dockerfile % ls tilt_modules 
extensions.json snyk

Une fois l’extension Snyk chargée, nous pouvons l’utiliser pour intégrer Snyk à notre workflow Tilt. L’extension prend en charge les trois modes proposés par la CLI Snyk : nous pouvons donc analyser les conteneurs, les dépendances des packages et les fichiers d’infrastructure as code, ici au format YAML Kubernetes. Elle est également conçue pour offrir un maximum de flexibilité et vous permettre de créer différents workflows dans Tilt.

Commençons par analyser notre fichier YAML Kubernetes. Dans le Tiltfile, nous appelons l’extension Snyk et la configurons pour analyser le fichier kubernetes.yaml :

snyk('kubernetes.yaml', 'iac', 'snyk-iac-manual', mode='info', trigger=’manual’)

L’extension accepte différents arguments. Pour consulter la documentation complète, reportez-vous au README. Ici, nous ciblons kubernetes.yaml, demandons une analyse IaC, attribuons un nom à cette ressource, définissons le mode sur info et configurons le déclenchement manuel. L’indicateur de mode peut être défini sur info ou gate. En mode info, la commande renvoie toujours le code de sortie zéro, même si des vulnérabilités sont détectées ; l’analyse n’est donc pas considérée comme un échec dans l’interface Tilt. Si nous définissons le déclenchement sur auto, l’analyse s’exécutera chaque fois que Tilt détectera une modification de kubernetes.yaml. En mode manuel, nous devrons la lancer depuis l’interface Tilt.

Après avoir enregistré le Tiltfile, nous voyons dans l’interface Tilt un nouveau lien dans la barre latérale, intitulé snyk-iac-manual.

Tableau de bord de terminal sombre affichant les étapes du déploiement Kubernetes, le suivi du déploiement des pods et les journaux du serveur en cours d’exécution

Cette entrée comporte un bouton d’actualisation, qui indique que nous pouvons l’exécuter manuellement. Allons-y.

Terminal affichant un test Snyk IaC analysant un fichier YAML Kubernetes et répertoriant des problèmes de sécurité liés à l’infrastructure en tant que code

Lorsque nous lançons l’analyse, nous voyons la CLI Snyk s’exécuter sur kubernetes.yaml, ainsi que les résultats dans l’interface. Plusieurs problèmes de sécurité potentiels ont été détectés dans le fichier, mais le test est toujours indiqué comme ayant réussi, puisque nous avions choisi le mode informatif. Voyons ce qui se passe si nous passons en mode gate. Dans le Tiltfile, modifiez la configuration de ce contrôle :

snyk('kubernetes.yaml', 'iac', 'snyk-iac-manual', mode='gate', trigger='manual')

Après la nouvelle exécution du Tiltfile, nous remarquons d’abord dans l’interface que le bouton d’actualisation de notre contrôle manuel est mis en évidence. Cela signifie qu’il n’est plus synchronisé avec les autres étapes et qu’il doit être actualisé.

Vue agrandie du panneau des ressources de l’interface Tilt, affichant un filtre ALL et des entrées récentes, notamment « (Tiltfile) », « example-nod... », « deploy » et « snyk-iac-manu... »

Relançons la configuration mise à jour :

Interface de terminal sombre affichant les journaux d’un déploiement Kubernetes, des commandes ayant échoué et la liste des états des conteneurs.

Puisque nous avons défini la ressource en mode gate, Tilt la signale comme un échec de build.

Examinons maintenant les différentes options d’intégration de l’analyse des conteneurs à notre workflow Tilt. Il est important de noter que Tilt utilise des tags immuables : chaque build créé reçoit un tag unique, qui est également transmis à Kubernetes. Cela accélère le processus de déploiement. Pour en savoir plus, consultez cette page.

Par défaut, la fonction docker_build génère de nombreux tags immuables, que vous pouvez voir en examinant les images dans Docker :

matt@Mattbook 2-optimized-dockerfile % docker images
REPOSITORY                             TAG                     IMAGE ID       CREATED          SIZE
localhost:60923/example-nodejs-image   tilt-d30fcbf90266f17c   d30fcbf90266   2 minutes ago    918MB
localhost:60923/example-nodejs-image   tilt-7285dc3648babacf   7285dc3648ba   6 minutes ago    918MB
localhost:60923/example-nodejs-image   tilt-b897a2c5ce4f7431   b897a2c5ce4f   10 minutes ago   918MB
localhost:60923/example-nodejs-image   tilt-a470f2969ce1ee4c   a470f2969ce1   11 minutes ago   918MB
localhost:60923/example-nodejs-image   tilt-9d3e699a8bfe9d37   9d3e699a8bfe   24 minutes ago   918MB
localhost:60923/example-nodejs-image   tilt-2a3065202354b6c4   2a3065202354   26 minutes ago   918MB
localhost:60923/example-nodejs-image   tilt-33de7ba78c394971   33de7ba78c39   34 minutes ago   918MB

Cela pose également un problème pour l’analyse : il n’existe actuellement aucune fonction intégrée permettant d’accéder à ces tags ailleurs dans le Tiltfile. Or, nous devons connaître le tag de l’image pour pouvoir l’analyser avec Snyk.

Nous avons toutefois plusieurs options pour y remédier. Tilt fournit une fonction qui permet de définir vos propres étapes de build, que nous pouvons utiliser pour créer notre workflow.

Commençons par configurer une étape d’analyse manuelle. Nous allons rendre le tag de notre image mutable : nous lui attribuerons un tag fixe que nous utiliserons pour tous nos builds. Tilt attribuera ensuite un tag immuable au build avant son déploiement.

Dans notre Tiltfile, supprimons la fonction docker_build et remplaçons-la par une fonction custom_build :

custom_build('example-nodejs-image', 'docker build -t $EXPECTED_REF .', ['.'], tag='dev')

Nous allons donc appeler Docker, comme le ferait la fonction docker_build, mais en ajoutant la variable d’environnement spéciale $EXPECTED_REF. Nous avons également spécifié tag=’dev’,, que Tilt utilisera pour renseigner cette variable. Une fois le Tiltfile enregistré, nous constatons que Tilt a créé une image avec un tag mutable, puis lui a attribué un nouveau tag :

matt@Mattbook 2-optimized-dockerfile % docker images
REPOSITORY                             TAG                     IMAGE ID       CREATED          SIZE
example-nodejs-image                   dev                     ffea1aaff7dd   10 seconds ago   918MB
localhost:60923/example-nodejs-image   tilt-ffea1aaff7ddcc22   ffea1aaff7dd   10 seconds ago   918MB

Nous avons ici défini comme cible le tag connu, spécifié le mode conteneur, nommé l’analyse et choisi le mode informatif. Comme le Dockerfile se trouve dans notre répertoire, nous pouvons également l’ajouter à l’argument extra_opts pour obtenir des suggestions d’images de base.

Tableau de bord sombre des ressources affichant un champ de filtre et quatre ressources répertoriées : (Tiltfile), example-nodejs, deploy et snyk-cnr-manual.

Notre barre latérale comporte maintenant l’analyse manuelle du conteneur. Nous pouvons la lancer.

Rapport d’analyse des vulnérabilités d’une image de conteneur répertoriant les dépendances testées, le nombre de vulnérabilités et leur niveau de gravité.

Nous avons donc lancé manuellement l’analyse du conteneur. Mais comment l’exécuter automatiquement à chaque build Tilt ? Ce scénario est un peu plus complexe, car Tilt ne gère pas encore très bien les dépendances avant ou après le build. Nous pourrions appeler Snyk directement dans la fonction custom_build, mais l’objectif de l’extension est de proposer aux utilisateurs une méthode flexible pour intégrer les analyses Snyk à leurs workflows.

Commençons par modifier notre fonction custom_build. Supprimons l’argument tag=’dev’ : $EXPECTED_REF prendra alors la valeur du tag immuable généré automatiquement par Tilt. À chaque build, nous écrirons ensuite ce tag dans un fichier de notre système de fichiers. Nous utilisons ici printf pour éviter qu’un saut de ligne soit ajouté à la fin du tag lors de son écriture dans le fichier. 

custom_build('example-nodejs-image', 'docker build -t $EXPECTED_REF . && printf $EXPECTED_REF > /tmp/ref.txt', ['.'])

Nous allons également ajouter une fonction pour récupérer le tag depuis le fichier. La fonction read_file renvoie un blob : nous devons donc le convertir en chaîne de caractères, puis supprimer les éventuels sauts de ligne :

def get_tag ():
return str(read_file('/tmp/ref.txt', default=''))

Nous allons ensuite appeler l’extension Snyk en définissant comme cible le résultat de notre fonction get_tag, en activant le déclenchement automatique et en créant une dépendance avec le fichier dans le système de fichiers. Ainsi, chaque fois que ce fichier est modifié, notre extension s’exécute.

snyk(get_tag(), 'container', 'snyk-cnr-auto', test_deps='ref.txt', mode='info', extra_opts='--file=Dockerfile', trigger='auto')

Une fois le Tiltfile à nouveau enregistré, nous constatons qu’à chaque exécution de notre build, nous déclenchons également automatiquement

l’analyse Snyk.

Tableau de bord du terminal affichant les journaux de déploiement et une modification du Tiltfile déclenchant une nouvelle analyse

Enfin, examinons l’analyse des dépendances du code source de notre application. Comme il s’agit d’une application Node.js qui utilise yarn comme gestionnaire de packages, le dossier contient un fichier yarn.lock qui indique les versions des modules utilisés.

matt@Mattbook 2-optimized-dockerfile % ls
Dockerfile Tiltfile index.js kubernetes.yaml now.py package.json public ref.txt start-time.txt tilt_modules views yarn.lock

Nous pouvons l’analyser automatiquement avec Snyk pour détecter les vulnérabilités :

snyk('.', 'oss', 'snyk-oss-auto', mode='info', trigger='auto')
Interface de terminal sombre affichant une compilation et une mise à jour réussies avec Tilt, ainsi qu’une analyse des vulnérabilités ne détectant aucun chemin vulnérable connu

Dans cet exemple, l’application est très simple et aucune de ses dépendances ne présente de vulnérabilité. Tout est donc en ordre !

Tilt est un outil fantastique qui peut accélérer considérablement le développement local d’applications Kubernetes basées sur des microservices. Ses nombreuses options de configuration vous permettent de créer le workflow qui vous convient parfaitement.

Dans cet article, nous avons découvert différentes façons d’intégrer l’analyse Snyk à vos workflows Tilt à l’aide de l’extension Snyk pour Tilt, afin de sécuriser vos applications cloud natives et leur configuration. Créez un compte Snyk gratuit et essayez-la.

Lancez-vous dans les compétitions Capture The Flag

Apprenez à résoudre des défis Capture The Flag en regardant à la demande notre atelier virtuel d’initiation.

Lire la suite

blog feature snyk container custom base recommendations
Blog

Découvrez Evo Agentic AppSec : une sécurité au rythme du développement de l’IA

Le développement piloté par l’IA dépasse les workflows de sécurité traditionnels. L’AppSec agentique de Snyk s’appuie sur des agents de sécurité pour détecter, corriger et vérifier les vulnérabilités en continu, tandis que les équipes définissent les exigences.

feature insights context
Blog

Les attaques autonomes sont déjà là. La défense doit suivre leur rythme.

Les attaquants autonomes réduisent la fenêtre de défense. Découvrez comment la découverte, la correction, la validation et la prévention continues peuvent aider les équipes de sécurité à suivre le rythme.

Blog

Pourquoi les agents de codage IA créent-ils sans cesse des failles de contrôle d’accès ?

Les agents de codage IA peuvent générer une logique d’autorisation qui compile et passe la revue, tout en exposant les données d’un tenant à un autre. Découvrez pourquoi les failles de contrôle d’accès sont difficiles à détecter et comment les prévenir.