Veröffentlicht am: 5. Februar 2025
8 Minuten Lesezeit
Tutorial für Security-Scanner in luftspaltgesicherten Umgebungen: Offline-Installation und Vulnerability-Detection ohne externe Abhängigkeiten.

Air-Gapped-Umgebungen (luftspaltgesicherte Systeme) sind Computer-Netzwerke oder Systeme, die physisch von unsicheren Netzwerken isoliert sind – beispielsweise vom öffentlichen Internet oder unsicheren lokalen Netzwerken. Diese Isolation dient als Sicherheitsmaßnahme zum Schutz sensibler Daten und kritischer Systeme vor externen Cyberbedrohungen:
Für deutsche Unternehmen und kritische Infrastrukturen ist dieser Ansatz besonders relevant: Die NIS2-Richtlinie fordert gemäß Artikel 21 systematisches Schwachstellenmanagement – auch für luftspaltgesicherte Systeme. Kritische Einrichtungen wie Energieversorger, Wasserwerke oder Behörden setzen auf Air-Gapped-Umgebungen, um KRITIS-Anforderungen zu erfüllen und Angriffsflächen zu minimieren. Offline-Security-Scanning gewährleistet, dass auch isolierte Systeme kontinuierlich auf Vulnerabilities überprüft werden, ohne die physische Netzwerktrennung zu kompromittieren.
Die Compliance-Anforderungen sind konkret: ISO 27001 A.12.6.1 verlangt technisches Schwachstellenmanagement, DSGVO Artikel 32 fordert angemessene technische Maßnahmen zur Verarbeitungssicherheit, und BSI IT-Grundschutz SYS.1.6 definiert Container-Sicherheitsanforderungen. GitLabs Offline-Scanner-Deployment erfüllt diese Vorgaben durch systematische Vulnerability-Detection ohne externe Abhängigkeiten.
Auch wenn Air-Gapped-Systeme isoliert sind, können Schwachstellen auftreten. Regelmäßiges Security-Scanning hilft, diese zu identifizieren, bevor sie ausgenutzt werden. Dieses Tutorial zeigt, welche Security-Scanner GitLab bereitstellt und wie diese in Umgebungen mit eingeschränkter Konnektivität implementiert werden.
GitLab bietet verschiedene Security-Scanner für den gesamten Application-Lifecycle. Die folgenden Scanner unterstützen Air-Gapped-Umgebungen:
📖 Scanner-Dokumentation
Detaillierte Konfigurationsanleitungen für jeden Scanner finden sich in der GitLab-Dokumentation (englischsprachig):
Standardmäßig laden GitLab Self-Managed-Instanzen Security-Scanner-Images aus der öffentlichen GitLab Container Registry (registry.gitlab.com) und speichern diese in der integrierten lokalen GitLab Container Registry. Der folgende Workflow demonstriert dies anhand einer Pipeline, die auf einem Sample-Projekt nach Secrets scannt:
include:
- template: Jobs/Secret-Detection.gitlab-ci.yml
Bei Ausführung in einer internetverbundenen GitLab-Instanz ist der Job erfolgreich:

Wenn jedoch der Internet-Zugriff zur VM mit GitLab deaktiviert wird, schlägt der secret-detection-Job fehl, da das Container-Image nicht heruntergeladen werden kann:

Die Cache-Strategie bietet eine Teillösung: Bei Pull-Image-Policy if-not-present statt always lässt sich die gecachte Scanner-Version laden, falls diese zuvor bei Internet-Zugriff ausgeführt wurde:

Diese Cache-Strategie ist jedoch nicht systematisch reproduzierbar für echte Air-Gapped-Umgebungen. Die folgende Lösung zeigt den vollständigen Offline-Deployment-Workflow.
Die Ausführung von Security-Scannern in Air-Gapped-Umgebungen erfordert:
Dieses Tutorial lässt sich in jeder GitLab Self-Managed EE-Instanz nachvollziehen (auch solchen, die nicht luftspaltgesichert sind), um Image-Transfer und -Ausführung in Air-Gapped-Umgebungen zu erlernen.
Der Prozess zum Laden von Security-Scannern in Air-Gapped-Umgebungen umfasst drei Schritte:
Für dieses Tutorial wird eine GitLab-EE-Instanz in einer Google Compute VM verwendet, bei der durch Firewall-Regeln der gesamte ausgehende Internet-Traffic blockiert wird:
# Egress-Firewall-Regel zum Blockieren allen ausgehenden Internet-Traffics
$ gcloud compute firewall-rules create deny-internet-egress \
--direction=EGRESS \
--priority=1000 \
--network=default \
--action=DENY \
--rules=all \
--destination-ranges=0.0.0.0/0 \
--target-tags=no-internet
# Allow-Regel für internen Traffic mit höherer Priorität
$ gcloud compute firewall-rules create allow-internal-egress \
--direction=EGRESS \
--priority=900 \
--network=default \
--action=ALLOW \
--rules=all \
--destination-ranges=10.0.0.0/8,192.168.0.0/16,172.16.0.0/12 \
--target-tags=no-internet
# Tag auf VM anwenden
$ gcloud compute instances add-tags YOUR_VM_NAME \
--zone=YOUR_ZONE \
--tags=no-internet
Validierung der Isolation: Nach SSH-Verbindung zur VM lässt sich verifizieren, dass keine Verbindung zu registry.gitlab.com möglich ist:
# Nachweis: Kein Zugriff auf GitLab Container Registry
$ ping registry.gitlab.com
PING registry.gitlab.com (35.227.35.254) 56(84) bytes of data.
^C
--- registry.gitlab.com ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2031ms
Hinweis: Ingress ist weiterhin erlaubt, um Dateien zu kopieren und per SSH auf die Maschine zuzugreifen.
In einer separaten Umgebung mit Internet-Zugang wird das Container-Image für Secret Detection heruntergeladen. Für dieses Beispiel wird Podman Desktop verwendet, alternativ lassen sich Docker Desktop oder andere Tools nutzen.
Container-Image pullen:
$ podman pull registry.gitlab.com/security-products/secrets:6
Trying to pull registry.gitlab.com/security-products/secrets:6...
Getting image source signatures
Copying blob sha256:999745130ac045f2b1c29ecce088b43fc4a95bbb82b7960fb7b8abe0e3801bf8
Copying blob sha256:a4f7c013bb259c146cd8455b7c3943df7ed84b157e42a2348eef16546d8179b1
Copying blob sha256:1f3e46996e2966e4faa5846e56e76e3748b7315e2ded61476c24403d592134f0
Copying blob sha256:400a41f248eb3c870bd2b07073632c49f1e164c8efad56ea3b24098a657ec625
Copying blob sha256:9090f17a5a1bb80bcc6f393b0715210568dd0a7749286e3334a1a08fb32d34e6
Copying blob sha256:c7569783959081164164780f6c1b0bbe1271ee8d291d3e07b2749ae741621ea3
Copying blob sha256:20c7ca6108f808ad5905f6db4f7e3c02b21b69abdea8b45abfa34c0a2ba8bdb5
Copying blob sha256:e8645a00be64d77c6ff301593ce34cd8c17ffb2b36252ca0f2588009a7918d2e
Copying config sha256:0235ed43fc7fb2852c76e2d6196601968ae0375c72a517bef714cd712600f894
Writing manifest to image destination
WARNING: image platform (linux/amd64) does not match the expected platform (linux/arm64)
0235ed43fc7fb2852c76e2d6196601968ae0375c72a517bef714cd712600f894
$ podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry.gitlab.com/security-products/secrets 6 0235ed43fc7f 4 hours ago 85.3 MB
Image als Tarball speichern:
$ podman save -o secret-detection.tar registry.gitlab.com/security-products/secrets:6
$ chmod +r secret-detection.tar
$ ls -al secret-detection.tar
-rw-r--r--@ 1 fern staff 85324800 Jan 10 10:25 secret-detection.tar
Alternative Methode
Das offizielle GitLab-Template lässt sich in einer Umgebung mit Internet-Zugang verwenden, um Container-Images herunterzuladen und als Job-Artefakte zu speichern oder in die Container Registry des Projekts zu pushen. Details in der Offline Deployments Dokumentation.
Der Tarball-Transfer in die Air-Gapped-Umgebung lässt sich auf verschiedene Weisen durchführen:
Für dieses Tutorial wird SCP (Secure Copy Protocol) verwendet, um das Tarball direkt auf die VM zu übertragen, die keinen Egress-Zugriff hat, aber Ingress erlaubt. Wichtig: In Produktionsumgebungen sollten die Sicherheitsrichtlinien und Transfer-Verfahren der eigenen Organisation für Air-Gapped-Umgebungen konsultiert werden.
Cache-Status verifizieren:
Vor dem Transfer werden Docker-Images für Secret Detection auf der GitLab-Instanz gelöscht, um sicherzustellen, dass die Lösung ohne Cache funktioniert:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry.gitlab.com/security-products/secrets 6 0235ed43fc7f 9 hours ago 84.8MB
registry.gitlab.com/security-products/secrets <none> 16d88433af61 17 hours ago 74.9MB
$ docker image rmi 16d88433af61 -f
Untagged: registry.gitlab.com/security-products/secrets@sha256:f331da6631d791fcd58d3f23d868475a520f50b02d64000e2faf1def66c75d48
Deleted: sha256:16d88433af618f0b405945031de39fe40b3e8ef1bddb91ca036de0f5b32399d7
Deleted: sha256:1bb06f72f06810e95a70039e797481736e492201f51a03b02d27db055248ab6f
Deleted: sha256:a5ef2325ce4be9b39993ce301f8ed7aad1c854d7ee66f26a56a96967c6606510
Deleted: sha256:f7cdac818a36d6c023763b76a6589c0db7609ca883306af4f38b819e62f29471
Deleted: sha256:5eabf4d47287dee9887b9692d55c8b5f848b50b3b7248f67913036014e74a0e9
Deleted: sha256:51b7cb600604c0737356f17bc02c22bac3a63697f0bf95ba7bacb5b421fdb7da
Deleted: sha256:1546193b011d192aa769a15d3fdd55eb4e187f201f5ff7506243abb02525dc06
Deleted: sha256:1ea72408d0484c3059cc0008539e6f494dc829caa1a97d156795687d42d9cb57
Deleted: sha256:1313ee9da7716d85f63cfdd1129f715e9bbb6c9c0306e4708ee73672b3e40f26
Deleted: sha256:954ebfd83406f0dfed93eb5157ba841af5426aa95d4054174fff45095fd873a1
$ docker image rmi 0235ed43fc7f -f
Untagged: registry.gitlab.com/security-products/secrets:6
Deleted: sha256:0235ed43fc7fb2852c76e2d6196601968ae0375c72a517bef714cd712600f894
Deleted: sha256:f05f85850cf4fac79e279d93afb6645c026de0223d07b396fce86c2f76096c1f
Deleted: sha256:7432b0766b885144990edd3166fbabed081be71d28d186f4d525e52729f06b1f
Deleted: sha256:2c6e3361c2ee2f43bd75fb9c7c12d981ce06df2d51a134965fa47754760efff0
Deleted: sha256:7ad7f7245b45fbe758ebd5788e0ba268a56829715527a9a4bc51708c21af1c7f
Deleted: sha256:3b73a621115a59564979f41552181dce07f3baa17e27428f7fff2155042a1901
Deleted: sha256:78648c2606a7c4c76885806ed976b13e4d008940bd3d7a18b52948a6be71b60d
Deleted: sha256:383d4a6dc5be9914878700809b4a3925379c80ab792dfe9e79d14b0c1d6b5fad
Problem demonstrieren: Der Job wird erneut ausgeführt, um den Fehler zu zeigen:

Von der lokalen Maschine wird die Datei per SCP auf die GitLab-Instanz übertragen:
$ gcloud compute scp secret-detection.tar INSTANCE:~ --zone=ZONE
secret-detection.tar 100% 81MB 21.5MB/s 00:03
Per SSH auf die VM verbinden und das Docker-Image laden:
$ gcloud compute ssh INSTANCE --zone=ZONE
$ sudo docker load -i secret-detection.tar
c3c8e454c212: Loading layer [==================================================>] 2.521MB/2.521MB
51e93afaeedc: Loading layer [==================================================>] 32.55MB/32.55MB
e8a25e39bb30: Loading layer [==================================================>] 221.2kB/221.2kB
390704968493: Loading layer [==================================================>] 225.8kB/225.8kB
76cf57e75f63: Loading layer [==================================================>] 17.64MB/17.64MB
c4c7a681fd10: Loading layer [==================================================>] 4.608kB/4.608kB
f0690f406157: Loading layer [==================================================>] 24.01MB/24.01MB
Loaded image: registry.gitlab.com/security-products/secrets:6
Die Pipeline wird manuell erneut ausgeführt. Der Scanner wird aus dem Cache geladen, und nach Abschluss zeigt sich der erfolgreiche Secret-Detection-Job:

Falls das Image von einem anderen Standort geladen oder Images anders getaggt werden sollen, lässt sich die Konfiguration anpassen:
include:
- template: Jobs/Secret-Detection.gitlab-ci.yml
variables:
SECURE_ANALYZERS_PREFIX: "localhost:5000/analyzers"
Weitere Konfigurationsdetails
Die vollständige Offline-Environments-Dokumentation bietet zusätzliche Optionen: Offline Deployments Guide
Nach Abschluss des Scanners im Default-Branch wird der Vulnerability Report mit allen Findings befüllt. Der Zugriff erfolgt über die Seitenleiste: Secure > Vulnerability Report

Der Vulnerability Report bietet:
Zwei Vulnerabilities wurden vom Secret-Detection-Scanner erkannt. Ein Klick auf eine Vulnerability öffnet die Vulnerability-Seite:

Die Vulnerability-Seite bietet Details für Triage und Remediation:
Der in diesem Tutorial gezeigte Workflow lässt sich auf alle GitLab-Security-Scanner anwenden, die Air-Gapped-Umgebungen unterstützen. Jeder Scanner erfordert das Herunterladen des entsprechenden Container-Images, Transfer in die Offline-Umgebung und Loading in die lokale Registry.
📚 Weiterführende Ressourcen
Die folgenden GitLab-Ressourcen sind in englischer Sprache verfügbar:
Produktinformationen:
Technische Dokumentation:
- Offline Deployments Guide
- Application Security Documentation
- Scanner-spezifische Offline-Konfigurationen (SAST, DAST, Container Scanning, etc.)