Skip to main content

In this article

Sichern Sie Ihre Kubernetes-Anwendungsentwicklung mit Snyk und Tilt

Artikel von

11. Januar 2021

0 Min. Lesezeit

Die Entwicklung von Kubernetes-Anwendungen kann schwierig sein. Oft arbeiten wir mit Microservice-Architekturen, die viele bewegliche Teile umfassen. Gleichzeitig entwickeln wir die Cluster-Konfiguration, die alles miteinander verbindet. Workflows für schnelle Iterationen und Tests können dabei komplex und für Engineering-Teams schwer zu verwalten werden.

Hier kommen Tools wie Tilt ins Spiel.

Was ist Tilt?

Tilt ist ein großartiges Tool für Entwicklerinnen und Entwickler, die mit Kubernetes-Anwendungen arbeiten. Es bietet einen hochgradig konfigurierbaren lokalen Deployment-Workflow, der während der Arbeit in Echtzeit aktualisiert wird. Anstatt eine zentrale Gruppe von Umgebungen zu verwalten, in der alle Entwicklerinnen und Entwickler arbeiten, stellt Tilt jeder Person eine eigene lokale Umgebung mit extrem schnellen Deployment-Zeiten bereit und ermöglicht es, den eigenen Workflow anzupassen.

Tilt wird mithilfe einer auf Starlark basierenden Tilftfile konfiguriert und bietet eine anpassbare GUI- und CLI-Umgebung sowie eine umfangreiche API. Dadurch haben Sie viel Flexibilität, Ihre Workflows und die Informationen festzulegen, die Ihnen während der Entwicklung Ihrer Anwendung angezeigt werden sollen. Tilt unterstützt außerdem benutzerdefinierte Erweiterungen, sodass Sie Ihre Entwicklungsumgebung noch flexibler einrichten und ausführen können.

Mein Kollege Shai Mendel hat eine Tilt-Erweiterung beigesteuert, mit der sich Snyk in Ihrer Tiltfile ausführen lässt. Vor Kurzem habe ich die Erweiterung aktualisiert und verschiedene Möglichkeiten erkundet, Snyk in Ihre Tilt-Workflows zu integrieren. Sehen wir uns an, wie das funktioniert.

Erste Schritte

Zunächst möchten wir lokal einen Kubernetes-Cluster samt Registry bereitstellen. Tilt bietet ein Tool, mit dem das ganz einfach geht. Ctlptl unterstützt verschiedene Möglichkeiten, Kubernetes lokal auszuführen, darunter miniKube, Kind und Docker Desktop. Ich verwende Kind, das Sie zuerst installieren müssen.

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

Mit diesem Befehl wird mithilfe von Kind ein Kubernetes-Cluster bereitgestellt. Außerdem wird eine Registry in Ihrem lokalen Docker eingerichtet und alles Notwendige konfiguriert, damit Kubernetes sie verwenden kann. Sobald der Befehl abgeschlossen ist, sehen wir in Docker, dass beides bereitgestellt wurde:

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

Als Nächstes klone ich eine Beispielanwendung, die ich zum Testen verwenden kann. Ich nutze das Node.js-Beispiel, das Tilt bereitstellt. Diese Anwendung enthält mehrere Beispiel-Tiltfiles mit Best Practices für Tilt sowie einen Blogbeitrag, in dem alle Beispiele erläutert werden.

Für diesen Beitrag verwenden wir das optimierte Dockerfile-Beispiel. Sehen wir uns die bereitgestellte Tiltfile an:

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', '.')

Sehen wir uns an, was diese Tiltfile macht: Sie verwendet die Funktion docker_build, um aus dem Dockerfile in diesem Ordner das Image example-node-js-image zu erstellen. Außerdem erstellt sie mithilfe der Datei kubernetes.yaml eine k8s_yaml-Ressource und verknüpft damit eine k8s_resource mit einer Portweiterleitung zu Port 8000. Zusätzlich ist eine local_resource definiert, die die aktuelle Uhrzeit in eine Datei schreibt und eine Abhängigkeit zu k8s_resource erstellt. Dadurch wird sie vor jeder Aktualisierung der Ressource ausgeführt und dient dazu, die Deployment-Zeit zu messen. Weitere Informationen zu allen von der Tiltfile-API unterstützten Funktionen finden Sie in der Dokumentation.

Außerdem haben wir ein Dockerfile, das die benötigten Abhängigkeiten unserer App mit yarn installiert:

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" ]

Unsere Anwendung selbst ist eine sehr einfache Node.js-Anwendung, die lediglich eine Webseite auf Port 8000 anzeigt:

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)

Nachdem unser Cluster nun bereitgestellt ist, können wir Tilt starten. Führen Sie dazu in dem Ordner mit unserer Tiltfile folgenden Befehl aus:

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

Wenn Tilt im Terminal läuft, können wir die Leertaste drücken, um die Tilt-Benutzeroberfläche im Browser zu öffnen.

Dunkles browserbasiertes Deployment-Protokoll mit Kubernetes-YAML-Injektion, Rollout-Tracking und Service-Statusanzeigen

Tilt führt die Tiltfile aus und startet alle definierten Ressourcen. In unserem Fall wird also das Image erstellt, in Kubernetes bereitgestellt und eine Portweiterleitung für den Zugriff auf die Anwendung eingerichtet.

Mit kubectl können wir sehen, dass der Pod mit der Anwendung ausgeführt wird:

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

Wenn wir jetzt http://localhost:8000 aufrufen, sehen wir die Anwendung:

Browserseite mit drei Comic-Katzen auf einem Blitz über dem Text „Hallo, Katzen!“ und Informationen zur Dauer der Bereitstellung.

Sobald Tilt läuft, lösen Änderungen an überwachten Verzeichnissen und Dateien eine erneute Ausführung der Tiltfile aus. Die Anwendung wird dann automatisch neu erstellt und bereitgestellt.

Sehen wir uns nun an, wie wir Snyk in diesen Workflow integrieren können. Falls noch nicht geschehen, müssen Sie die Snyk CLI installieren und ein kostenloses Snyk-Konto erstellen.

Zunächst müssen wir die Snyk-Erweiterung laden. Dazu fügen wir Folgendes in die Tiltfile ein:

# -*- 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', '.')

Dabei führt Tilt die Tiltfile automatisch erneut aus. In unserem Anwendungsordner erscheint dann ein Verzeichnis namens tilt_modules, das die Snyk-Erweiterung enthält. Erweiterungen sind selbst Tiltfiles, die in die Haupt-Tiltfile geladen werden.

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

Nachdem die Snyk-Erweiterung geladen wurde, können wir damit Snyk in unseren Tilt-Workflow integrieren. Die Erweiterung unterstützt alle drei Modi der Snyk CLI. Damit können wir Container, Paketabhängigkeiten und Infrastructure-as-Code-Dateien scannen – in diesem Fall Kubernetes-YAML. Sie ist außerdem möglichst flexibel gestaltet, sodass Sie in Tilt verschiedene Workflows einrichten können.

Sehen wir uns zunächst an, wie wir unser Kubernetes-YAML scannen. Dazu rufen wir in der Tiltfile die Snyk-Erweiterung auf und konfigurieren sie so, dass sie die Datei kubernetes.yaml scannt:

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

Die Erweiterung unterstützt verschiedene Argumente. Die vollständige Dokumentation finden Sie in der README. Hier geben wir kubernetes.yaml als Ziel an, legen fest, dass ein IaC-Scan durchgeführt werden soll, vergeben einen Namen für die Ressource, setzen den Modus auf info und den Trigger auf manual. Für das Modus-Flag können Sie info oder gate festlegen. Im Modus info wird immer der Rückgabecode null ausgegeben, auch wenn Schwachstellen gefunden werden. Der Scan gilt daher in der Tilt-Benutzeroberfläche nicht als fehlgeschlagen. Wenn wir den Trigger auf auto setzen, wird der Scan jedes Mal ausgeführt, wenn Tilt Änderungen an kubernetes.yaml erkennt. Im manuellen Modus müssen wir den Scan hingegen über die Tilt-Benutzeroberfläche starten.

Nachdem wir die Tiltfile gespeichert haben, sehen wir in der Tilt-Benutzeroberfläche in der Seitenleiste einen neuen Eintrag namens snyk-iac-manual.

Dunkles Terminal-Dashboard mit Kubernetes-Bereitstellungsschritten, Pod-Rollout-Tracking und laufenden Server-Logs

Bei diesem Eintrag sehen wir eine Aktualisierungsschaltfläche. Sie zeigt an, dass wir den Scan manuell ausführen können. Starten wir ihn also.

Terminal mit einem Snyk IaC-Test, der eine Kubernetes-YAML-Datei scannt und Sicherheitsprobleme in der Infrastructure as Code auflistet

Beim Ausführen sehen wir, dass die Snyk CLI kubernetes.yaml scannt und die Ergebnisse in der Benutzeroberfläche angezeigt werden. Obwohl wir mehrere potenzielle Sicherheitsprobleme in der Datei gefunden haben, wird der Test weiterhin als erfolgreich abgeschlossen angezeigt, weil wir den Modus auf informational gesetzt haben. Sehen wir uns an, was passiert, wenn wir den Modus auf gate setzen. Ändern Sie dazu die Konfiguration dieser Prüfung in der Tiltfile:

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

Nach der erneuten Ausführung der Tiltfile sehen wir in der Benutzeroberfläche zunächst, dass die Aktualisierungsschaltfläche unserer manuellen Prüfung hervorgehoben ist. Das bedeutet, dass sie nicht mit den übrigen Phasen synchron ist und aktualisiert werden muss.

Herangezoomter Ressourcenbereich der Tilt-Benutzeroberfläche mit einem Filter „ALL“ und aktuellen Einträgen wie „(Tiltfile)“, „example-nod...“, „deploy“ und „snyk-iac-manu...“

Führen wir die aktualisierte Konfiguration erneut aus:

Dunkle Terminal-Oberfläche mit Kubernetes-Bereitstellungsprotokollen, fehlgeschlagenen Befehlen und einer Liste von Containerstatus.

Da wir die Ressource jetzt auf gate gesetzt haben, erkennt Tilt dies als fehlgeschlagenen Build.

Sehen wir uns nun die Möglichkeiten an, Container-Scans in unseren Tilt-Workflow zu integrieren. Wichtig ist dabei, dass Tilt unveränderliche Tags verwendet. Jeder erstellte Build erhält ein eindeutiges Tag, das auch an Kubernetes weitergegeben wird. So wird der Deployment-Prozess beschleunigt. Weitere Einzelheiten dazu finden Sie hier.

Wenn Sie standardmäßig die Funktion docker_build verwenden, sehen Sie in Docker viele dieser unveränderlichen Tags:

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

Das erschwert auch das Scannen, denn Tilt bietet derzeit keine integrierte Funktion, mit der Sie an anderen Stellen in der Tiltfile auf diese Tags zugreifen können. Für den Scan mit Snyk müssen wir jedoch das Image-Tag kennen.

Dafür gibt es jedoch einige Möglichkeiten. Tilt bietet eine Funktion, mit der Sie eigene Build-Schritte definieren können. Diese können wir für unseren Workflow verwenden.

Sehen wir uns zunächst an, wie sich ein manueller Scan-Schritt einrichten lässt. Dazu machen wir unser Image-Tag veränderlich, legen also ein festes Tag fest, das für alle Builds verwendet wird. Vor dem Deployment versieht Tilt den Build dann erneut mit einem unveränderlichen Tag.

Entfernen wir in unserer Tiltfile die Funktion docker_build und ersetzen sie durch custom_build:

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

Hier rufen wir wie bei der Funktion docker_build Docker auf. Zusätzlich fügen wir die spezielle Umgebungsvariable $EXPECTED_REF hinzu und geben außerdem tag=’dev’, an. Tilt verwendet diesen Wert, um die Variable zu belegen. Sobald wir die Tiltfile speichern, sehen wir, dass Tilt ein Image mit einem veränderlichen Tag erstellt und es anschließend erneut taggt:

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

Hier haben wir das bekannte Tag als Ziel festgelegt, den Container-Modus angegeben, dem Scan einen Namen gegeben und den Modus auf informational gesetzt. Da sich das Dockerfile in unserem Verzeichnis befindet, können wir es außerdem dem Argument extra_opts hinzufügen, um Vorschläge für das Basis-Image zu erhalten.

Dunkles Ressourcen-Dashboard mit einem Filterfeld und vier aufgeführten Ressourcen: (Tiltfile), example-nodejs, deploy und snyk-cnr-manual.

Jetzt finden wir in der Seitenleiste unseren manuellen Container-Scan, den wir direkt ausführen können.

Bericht zum Schwachstellenscan eines Container-Images mit geprüften Abhängigkeiten, Anzahl der Schwachstellen und Schweregraden.

Wir haben nun einen Container-Scan manuell gestartet. Aber was, wenn wir ihn bei jedem Tilt-Build automatisch ausführen möchten? Das ist etwas anspruchsvoller, da Tilt Abhängigkeiten vor oder nach dem Build noch nicht ausgereift unterstützt. Wir könnten Snyk einfach direkt in der Funktion custom_build aufrufen. Ziel der Erweiterung ist es jedoch, Nutzerinnen und Nutzern eine flexible Möglichkeit zu bieten, Snyk-Scans in ihre Workflows zu integrieren.

Ändern wir zunächst unsere Funktion custom_build. Wir entfernen das Argument tag=’dev’. Dadurch wird $EXPECTED_REF auf das automatisch generierte, unveränderliche Tilt-Tag gesetzt. Bei jedem Build schreiben wir dieses Tag dann in eine Datei auf unserem Dateisystem. Wir verwenden hier printf, damit beim Schreiben des Tags keine Zeilenumbrüche angehängt werden.

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

Außerdem fügen wir eine Funktion hinzu, mit der wir das Tag aus der Datei auslesen. Die Funktion read_file gibt einen Blob zurück. Daher müssen wir ihn in einen String umwandeln und außerdem alle Zeilenumbrüche entfernen:

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

Anschließend rufen wir die Snyk-Erweiterung auf, setzen als Ziel die Ausgabe unserer Funktion get_tag, legen den Trigger auf auto fest und erstellen eine Abhängigkeit von der Datei im Dateisystem. Ändert sich die Datei, wird die Erweiterung also jedes Mal ausgeführt.

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

Sobald wir die Tiltfile erneut speichern, sehen wir, dass bei jedem Build automatisch auch der

Snyk-Scan gestartet wird.

Terminal-Dashboard mit Bereitstellungsprotokollen und einer Tiltfile-Änderung, die einen neuen Scan auslöst

Zum Schluss sehen wir uns an, wie wir die Abhängigkeiten im Quellcode unserer Anwendung scannen können. Da es sich um eine Node.js-Anwendung handelt, die yarn als Paketmanager verwendet, befindet sich in unserem Ordner eine yarn.lock-Datei. Sie enthält die Versionen der verwendeten Module.

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

Diese Datei können wir automatisch mit Snyk scannen, um nach Schwachstellen zu suchen:

snyk('.', 'oss', 'snyk-oss-auto', mode='info', trigger='auto')
Dunkle Terminaloberfläche mit erfolgreichem Tilt-Build und Update; der Schwachstellenscan meldet keine bekannten anfälligen Pfade

In diesem Fall ist die Anwendung sehr einfach, und keine ihrer Abhängigkeiten weist Schwachstellen auf. Alles in Ordnung!

Tilt ist wirklich ein hervorragendes Tool, das die lokale Entwicklung von Microservice-basierten Anwendungen für Kubernetes deutlich beschleunigen kann. Dank der zahlreichen Konfigurationsmöglichkeiten können Sie Ihren Workflow genau auf Ihre Anforderungen abstimmen.

In diesem Beitrag haben wir die verschiedenen Möglichkeiten kennengelernt, Snyk-Scans mithilfe der Snyk-Erweiterung für Tilt in Ihre Tilt-Workflows zu integrieren. So stellen Sie sicher, dass Ihre Cloud-nativen Anwendungen und Konfigurationen sicher sind. Erstellen Sie ein kostenloses Snyk-Konto und probieren Sie es aus.

Starten Sie mit Capture the Flag

Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.

Weiterlesen

blog feature snyk container custom base recommendations
Blog

Evo Agentic AppSec: Sicherheit, die mit der KI-Entwicklung Schritt hält

KI-gestützte Entwicklung überholt herkömmliche Security-Workflows. Snyk Agentic AppSec setzt Security-Agenten ein, die Schwachstellen kontinuierlich finden, beheben und überprüfen – während Menschen die Maßstäbe festlegen.

feature insights context
Blog

Autonome Angriffe sind bereits Realität. Die Verteidigung muss Schritt halten.

Autonome Angreifer verkürzen das Zeitfenster für die Verteidigung. Erfahren Sie, wie kontinuierliches Erkennen, Beheben, Validieren und Verhindern Sicherheitsteams helfen kann, Schritt zu halten.

Blog

Warum KI-Coding-Agenten immer wieder fehlerhafte Zugriffskontrollen schreiben

KI-Coding-Agenten können Autorisierungslogik erzeugen, die kompiliert und die Prüfung besteht, dabei aber die Daten eines Mandanten für einen anderen offenlegt. Erfahren Sie, warum fehlerhafte Zugriffskontrollen schwer zu erkennen sind und wie Sie sie verhindern.