Distroless Image'a Geçince Shell Kayboldu
docker exec sh artık bir seçenek değil

docker exec -it api sh 'not found' dedi. Beklenen. Distroless'te kabuk, paket yöneticisi, bazen libc üstü araçlar yok. İmaj küçüldü, yüzey daraldı, refleks kırıldı. Refleksi geri getirmek için apk add bash yapmak, distroless'i iptal etmektir.
Neyi kaybettim, neyi kazandım
Kaybettim: kutunun içinde ls, curl, less. Kazandım: RCE sonrası yürüyecek araç seti yok, katman sayısı düşük, CVE tarayıcısı 'os paketleri' diye 40 satır basmıyor. Bu takası yazmadan geçmek, ilk incident'te debug imajına geri göçmekle biter.
FROM gcr.io/distroless/dotnet8:nonroot
WORKDIR /app
COPY --from=build --chown=nonroot:nonroot /out ./
ENTRYPOINT ["dotnet", "Api.dll"]
Aynı dosyanın debug kardeşi: gcr.io/distroless/dotnet8:debug veya geçici bir busybox konteyneri, aynı network ve volume. kubectl debug bunu resmi yola koyar. Compose'ta ikinci bir servis, pid: service:api ile yetişir. Üretim imajına kabuk koyma.
Log ve sinyal
Kabuk olmayınca teşhis dışarı taşır: stdout JSON, metrics, kill -QUIT yerine process'in kendi dump ucu. Distroless, gözlemi erteleyen ekibi cezalandırır. Cezayı haklı buluyorum. sh ile içeri girip env dökmek zaten secret yazısının konusu.
Debug'ı nasıl bağlarım
Üretim imajına kabuk koymuyorum. Aynı digest'in yanında bir debug tag'i duruyor: aynı binary, üstüne busybox. Onu sadece o incident'te, o pod'da, o süre için koşturuyorum. kubectl debug ephemeral konteyneri aynı network namespace'e sokar. docker run --pid=container:api --network=container:api busybox:1.36 compose host'ta aynı iş.
services:
api:
image: api:1.8.0
read_only: true
api-debug:
image: busybox:1.36.1
pid: service:api
network_mode: service:api
profiles: ["debug"]
command: ["sleep", "3600"]
profiles: [debug] günlük compose up'a girmez. read_only: true üretim kökünü kilitler; debug konteyneri kendi yazılabilir katmanında durur. İki kutu, bir process ağacı. ls debug'tan bakılır, api imajına girilmez.
Nonroot da kabuksuzluğun parçası. Distroless nonroot UID 65532. Volume izni o UID'ye yazılmazsa kutu açılmaz. Bunu 'shell yok, debug edemedim' diye okumak yanlış; izin yanlış. chown build'de biter. Runtime'da chmod için kabuk aramak, izni ertelemektir.
Tek bir debug binary kopyalamak (busybox static) kabuk kadar geniş değildir ama yine bir araçtır. Onu TARGET=debug ile ayrı stage'te tutuyorum. docker build --target debug lokal, --target varsayılanı üretim. Aynı Dockerfile, iki kapı. Üretim kapısında busybox yok.
FROM gcr.io/distroless/dotnet8:nonroot AS app
WORKDIR /app
COPY --from=build --chown=nonroot:nonroot /out ./
ENTRYPOINT ["dotnet", "Api.dll"]
FROM app AS debug
COPY --from=busybox:1.36.1 /bin/busybox /busybox
# fail the PR if a shell landed in the production image
cid=$(docker create api:1.8.0)
docker export "$cid" | tar -t | grep -E '(^|/)(sh|bash|busybox|apk)$' && exit 1
docker rm "$cid" >/dev/null
docker export + tar -t bir dakikalık doğrulama. sh, bash, busybox, apk, apt-get listede yoksa iddia duruyor. Varsa PR geri döner. İddiayı slaytta bırakmıyorum; tar'da bırakıyorum. Tar yalan söylemez, pazarlama söyler. Kabuk özlemi, tar'da bir satır olarak kalmamalı.
rootfs'te /busybox/sh unutup bırakmak da oluyor. Image tar'ına bak. Distroless iddiası, tar'da kabuk yoksa duruyor. Varsa pazarlama.
Yorumlar
Yorumlar (0)
Yorumlar üyelere açık. Üye ol · Giriş yap