• 3.09.2026 21:27:25
  • Admin Admin

Yazılım eğitimi asistanlarının ürettiği kodu çalıştırırken container tek başına yeterli değildir. Ağ, dosya sistemi, süreç, kaynak kotası ve artefakt çıkışını ayrı yetenekler olarak sınırlandıran uygulanabilir bir sandbox tasarımı anlatılıyor.

Yazılım Eğitimi İçin Kod Çalıştıran Ajanlarda Sandbox Tasarımı

Yazılım eğitimi ajanı için tehdit modelini yeteneklere bölmek

Kod çalıştıran bir yazılım eğitimi ajanında tehdit modeli sadece 'öğrenci kodu zararlı olabilir' cümlesiyle bitmez. Girdi olarak repository arşivi, ortam değişkenleri, bağımlılık manifesti ve LLM'in ürettiği komut vardır. Çıktı olarak stdout, test raporu, derleme artefaktı ve bazen ekran görüntüsü üretilir. Her birini ayrı yetenek olarak modelleyin: çalışma alanına yazma, ağ bağlantısı, süreç başlatma, CPU zamanı, bellek ve sonuç dosyası dışa aktarma. Bu ayrım, örneğin testin ihtiyaç duymadığı halde `git`, `curl` veya bulut metadata IP'sine erişmesini varsayılan olarak engellemenizi sağlar.

  • Host dosya sistemi: yalnızca iş klasörünü read-write, dil çalışma zamanını read-only bağlayın.
  • Ağ: varsayılan olarak kapalı tutun; paket indirme gerekiyorsa kayıtlı bir proxy allowlist'i kullanın.
  • Süreç: PID namespace, non-root UID ve cgroup `pids.max` ile alt süreç çatallanmasını sınırlayın.
  • Çıkış: yalnızca önceden tanımlı `/out/result.json` ve `/out/artifacts/` yollarını toplayın.

Klasik Docker izolasyonu, host çekirdeğini paylaştığı için tek başına güvenlik sınırı değildir. Özellikle öğrenci kodunun `unshare`, genişletilmiş Linux capability'leri veya kernel saldırı yüzeylerini denemesi risklidir. Kubernetes üzerinde gVisor kullanan bir `RuntimeClass`, sistem çağrılarını user-space kernel katmanından geçirerek uygulama ile host kernel arasındaki doğrudan yüzeyi azaltır. Bu, güvenlik açığı olasılığını sıfırlamaz; ancak kontrol düzleminizin kabul ettiği risk varsayımını container kaçışı yerine sandbox katmanına taşır.

gVisor, seccomp ve Kubernetes ile somut çalışma profili

Aşağıdaki Pod tanımı, kod yürütmeyi ayrı namespace'te, gVisor runtime ile ve ayrı service account altında çalıştırır. `automountServiceAccountToken: false` kritik bir ayrıntıdır: aksi halde Pod içine otomatik bağlanan Kubernetes API token'ı, öğrenci kodunun cluster API'sine erişmeye çalışabileceği bir credential olur. `readOnlyRootFilesystem` aktifken derleyicilerin geçici dosyaları için yalnızca `/work` ve `/tmp` gibi açıkça bağlanmış alanlar kullanılmalıdır.

apiVersion: v1
kind: Pod
metadata:
  name: code-run-7f31
spec:
  runtimeClassName: gvisor
  automountServiceAccountToken: false
  restartPolicy: Never
  containers:
    - name: runner
      image: registry.example/runner-python:sha256-REPLACE_ME
      command: ["sh", "-lc", "python3 /work/main.py > /out/stdout.txt 2> /out/stderr.txt"]
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        runAsGroup: 10001
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop: ["ALL"]
        seccompProfile:
          type: RuntimeDefault
      resources:
        requests:
          cpu: "250m"
          memory: "256Mi"
        limits:
          cpu: "1"
          memory: "512Mi"
          ephemeral-storage: "1Gi"
      volumeMounts:
        - { name: work, mountPath: /work }
        - { name: out, mountPath: /out }
        - { name: tmp, mountPath: /tmp }
  volumes:
    - { name: work, emptyDir: { sizeLimit: 16Mi } }
    - { name: out, emptyDir: { sizeLimit: 8Mi } }
    - { name: tmp, emptyDir: { sizeLimit: 32Mi } }

`RuntimeDefault` seccomp profili yararlıdır ancak dil ekosistemlerinin gerçek syscall ihtiyacını ölçmeden daha sert özel profil yazmak sık yapılan bir hatadır. Örneğin Node.js veya JVM tabanlı derlemeler `clone`, `futex`, `epoll_wait` ve dosya metadata çağrılarına ihtiyaç duyar. Önce staging ortamında `tracee` veya `bpftrace` ile başarısız işi gözlemleyin, sonra sadece doğrulanmış çağrılardan bir allowlist türetin. Audit modunda çalışan bir profilin loglarına bakmadan `clone3` gibi çağrıları körlemesine engellemek, kernel sürümleri ve runtime davranışı değiştiğinde anlaşılması zor CI kırılmalarına yol açar.

Ağ ve bağımlılık erişimini egress proxy ile denetlemek

Ağın tamamen kapatılması tekrar üretilebilirlik için güçlüdür, fakat `pip install`, `npm ci` veya Maven çözümleme yapan derslerde yeterli olmayabilir. Bu durumda internet erişimini doğrudan açmak yerine, paket yöneticisini kurum içi bir proxy veya registry mirror'a yönlendirin. Proxy, paket adı, sürüm, hash ve kaynak kaydı tutmalıdır. Sadece alan adı allowlist'i yetersizdir; saldırgan izinli bir registry'de `latest` etiketini değiştirebilir. Lockfile ve checksum doğrulaması, aynı bağımlılık adının farklı byte içeriğiyle gelmesini engeller.

# Pod ortamında doğrudan internet yerine kayıtlı Python mirror kullanımı
export PIP_INDEX_URL=https://packages.internal.example/simple
export PIP_NO_INDEX=0
export PIP_REQUIRE_HASHES=1
python -m pip install --require-hashes -r requirements.txt

# requirements.txt satırı örneği
# requests==2.32.3 --hash=sha256:REPLACE_WITH_APPROVED_HASH

NetworkPolicy, Pod'un hangi IP veya namespace'e gidebildiğini sınırlar; HTTP isteğinin hangi paketi indirdiğini anlayamaz. Bu nedenle CNI düzeyindeki egress kuralını, proxy tarafındaki uygulama katmanı denetimiyle birlikte kullanın. Ayrıca `169.254.169.254` ve platformunuza ait metadata endpoint'lerini açıkça reddedin. Birçok ekip yalnızca public interneti engelleyip cluster içi DNS veya metadata erişimini unutuyor; oysa kısa ömürlü bir runner içindeki credential sızıntısının etkisi çoğu zaman bu iç endpoint'lerden gelir.

Süre, bellek ve çıktı taşmasını ölçerek sınır koymak

Kaynak sınırları kullanıcı deneyimi ile güvenlik arasında ölçülerek ayarlanmalıdır. CPU limiti tek başına sonsuz döngüyü sonlandırmaz; iş, CPU throttling altında uzun süre yaşamaya devam edebilir. Bu nedenle Pod `activeDeadlineSeconds`, cgroup bellek limiti, `pids.max` ve uygulama seviyesinde stdout byte limiti birlikte kullanılmalıdır. Python tarafında `subprocess.run(timeout=...)` sadece orkestratörün beklemesini keser; child process process group olarak öldürülmezse torun süreçler çalışmayı sürdürebilir.

import os
import signal
import subprocess

MAX_OUTPUT = 256 * 1024

proc = subprocess.Popen(
    ["python3", "/work/main.py"],
    stdout=subprocess.PIPE,
    stderr=subprocess.STDOUT,
    text=False,
    start_new_session=True,
)
try:
    output, _ = proc.communicate(timeout=8)
except subprocess.TimeoutExpired:
    os.killpg(proc.pid, signal.SIGKILL)
    output, _ = proc.communicate()
    raise RuntimeError("execution exceeded 8 seconds")

if len(output) > MAX_OUTPUT:
    raise RuntimeError("stdout exceeded 256 KiB")
open("/out/stdout.bin", "wb").write(output)

Limitleri tahminle değil, temsil' ders setiyle ölçün. Prometheus'ta `container_cpu_usage_seconds_total`, `container_memory_working_set_bytes`, `container_fs_usage_bytes` ve `container_oom_events_total` metriklerini runner kimliğiyle etiketleyin. İlk ölçümde örneğin CPU p95, bellek p99 ve başarı oranını kaydedin; ardından `activeDeadlineSeconds: 20`, `memory: 512Mi` ve 256 KiB stdout sınırını uygulayıp aynı iş setini yeniden çalıştırın. Başarı oranı düşmüşse sadece limiti yükseltmeyin: OOM olan işlerin bağımlılık indirme mi, derleme mi, test fixture üretimi mi yaptığını stderr ve cgroup olaylarından ayırın. JVM'in container bellek algısı veya Node paket kurulumunun geçici disk kullanımı, asıl darboğaz olabilir.

Artefakt toplama ve temizleme zincirinde zip slip riskini kapatmak

Sandbox çıkışı güvenilir kabul edilmemelidir. Öğrenci kodu `/out` altında `../../host-path` hedefli symlink, çok büyük sparse dosya veya milyonlarca küçük dosya üretebilir. Artefakt toplayıcı, arşivden önce `lstat` ile symlink'i reddetmeli, normalize edilmiş yolun hedef kök altında kaldığını doğrulamalı ve dosya sayısı ile toplam byte için kota uygulamalıdır. `tar.extractall()` veya `ZipFile.extractall()` fonksiyonlarını doğrulanmamış path'lerle doğrudan çağırmak zip slip açığı üretir.

from pathlib import Path
import os

OUT = Path("/out").resolve()
MAX_FILES = 100
MAX_BYTES = 8 * 1024 * 1024
seen_bytes = 0
files = []

for path in OUT.rglob("*"):
    if path.is_symlink() or not path.is_file():
        continue
    resolved = path.resolve()
    if OUT not in resolved.parents:
        raise ValueError(f"path escapes output root: {path}")
    size = path.stat().st_size
    seen_bytes += size
    if seen_bytes > MAX_BYTES or len(files) >= MAX_FILES:
        raise ValueError("artifact quota exceeded")
    files.append(resolved)

Her yürütme için benzersiz bir job ID üretin, Pod UID'sini, image digest'ini, lockfile hash'ini, seccomp profil adını ve sonuç durumunu append-only bir audit olayına yazın. TTL controller ile Job silinse bile olay kaydı kalmalıdır. Olayda ham öğrenci kodunu zorunlu olarak saklamak yerine içerik hash'i ve erişim kontrollü obje deposu anahtarı tutmak, hem tekrar üretilebilirlik hem de veri saklama politikasını uygulanabilir hale getirir.

Sık Sorulan Sorular

Yazılım eğitimi ajanında Docker container tek başına sandbox olur mu?

Hayır. Docker container host kernelini paylaşır. En azından non-root kullanıcı, capability drop, `allowPrivilegeEscalation: false`, seccomp, read-only root filesystem, cgroup kotaları ve ağ kısıtı gerekir. Daha yüksek izolasyon gereksiniminde Kubernetes `RuntimeClass` ile gVisor veya VM tabanlı runner değerlendirilmelidir.

Yazılım eğitimi için kod çalıştıran Pod'un internet erişimi nasıl açılmalı?

Pod'a doğrudan egress vermeyin. NetworkPolicy ile sadece paket proxy servisine erişim tanımlayın; `PIP_INDEX_URL`, npm registry veya Maven mirror ayarlarını bu servise yöneltin. Lockfile, sabit sürüm ve checksum doğrulaması olmadan allowlist registry kullanmak, değişen paket içeriği riskini çözmez.

LLM'in ürettiği kodda sonsuz döngü ve fork bomb nasıl sınırlandırılır?

Pod düzeyinde `activeDeadlineSeconds`, CPU ve bellek limitleri ile ephemeral storage kotası koyun. Süreç sayısını cgroup `pids.max` ile sınırlandırın. Orkestratörde komutu yeni process session ile başlatıp timeout sonrası `killpg` ile tüm process group'u sonlandırın. Ayrıca stdout için byte limiti uygulayın; aksi halde log toplama katmanı disk veya bellek tüketebilir.

Yazılım eğitimi sandbox performans limiti nasıl ölçülür?

Temsil' ödev ve testlerden oluşan sabit bir iş setini iki konfigürasyonla çalıştırın. Prometheus üzerinden CPU p95, bellek p99, OOM sayısı, tamamlanma süresi ve başarı oranını karşılaştırın. gVisor, sıkı seccomp veya düşük CPU limiti eklendikten sonra yalnızca ortalama süreye bakmayın; timeout ve OOM kaynaklarını stderr, cgroup event'leri ve artefakt boyutlarıyla sınıflandırın.

AI / LLM Discovery

Bu makale Opendart Akademi Yapay Zeka eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.

Opendart Akademi llms.txt