Tekrarlanabilir derlemeler tam olarak neyi kanıtlar?
Checksum sadece dosyanın bozulmadan ulaştığını gösterir. Tekrarlanabilir derleme ise ikili dosyanın kaynak kod ile birebir eşleştiğini kanıtlar. İki kavram arasındaki farkı öğrenin.
Tekrarlanabilir derlemelerin kanıtladığı durum
Tekrarlanabilir derlemeler tek bir dar gerçeği kanıtlar: size teslim edilen ikili dosya (binary), tam olarak bu kaynak kodun ürettiği dosyadır. Herkes aynı kaynağı alıp tekrar derleyebilir ve baytları karşılaştırabilir. Doğrulama işlemi, yalnızca yayıncının yapabildiği bir eylem olmaktan çıkar.
Reproducible Builds projesi bunu şu şekilde tanımlar: "Bir derleme; aynı kaynak kod, derleme ortamı ve derleme talimatları verildiğinde, herhangi bir tarafın tüm belirtilen çıktıların bit-bit aynı kopyalarını yeniden oluşturabilmesi durumunda tekrarlanabilirdir." Karşılaştırmanın kendisi bir hash işlemidir. Tüm zorluk, ortamı ve talimatları iki farklı makinenin aynı fikirde olacağı kadar sıkı bir şekilde sabitlemekte yatar.
Checksum neden bu soruyu yanıtlamaz
Yayınlanmış bir checksum, indirdiğiniz dosyanın referans değeriyle eşleştiğini ve dolayısıyla bozulmadan ulaştığını kanıtlar. Dosyayı kimin ürettiği hakkında hiçbir bilgi vermez. Apache'nin doğrulama kılavuzu hash değerleri konusunda nettir: "Dosyanın orijinalliğine dair hiçbir garanti sağlamazlar." Referans checksum değerinin güvenebileceğiniz bir kaynaktan gelmesi gerekir, aksi takdirde bir eşleşme hiçbir şey kanıtlamaz. Bu checksum üzerindeki bir imza, anahtarın gerçekten yayıncıya ait olduğunu doğruladığınızda, dosyanın anahtar sahibi tarafından imzalandığını kanıtlar. Ancak bunların hiçbiri, artifact oluşturulmadan önce neler yaşandığına dair bilgi vermez. Eğer yayıncının derleme sunucusu ele geçirilmişse, kötü niyetli binary dosyası da temiz bir dosya gibi checksum'lanır ve imzalanır; böylece sonraki tüm kontroller başarıyla geçer. Eğer bir geliştirici, repoya gönderilmemiş bir çalışma ağacından derleme yaparsa da durum aynıdır.
Aradaki boşluk budur. Kaynak kodunu okuyabilir, imzayı doğrulayabilir, checksum'ı kontrol edebilir ve yine de repoda hiç yer almamış bir kodu çalıştırıyor olabilirsiniz. Bu nedenle yeniden üretilebilirlik (reproducibility), bir indirmeyi yayınlanmış checksum'ı ile doğrulamaktan farklı bir konudur. Checksum aktarımı korur. Yeniden derleme ise aktarımdan önce gerçekleşen her şeyi korur.
Bu saldırı teorik değildir. 2020 SolarWinds Orion ihlali tam olarak bu noktada gerçekleşmiştir: derleme sistemi, kimsenin incelemediği kaynak koduna karşılık gelmeyen imzalı artifact'ler üretmiştir. İmza kontrollerinin tamamı başarıyla geçmiştir, çünkü imzalar artifact aşamasında başlar.
Tekrarlanabilir bir derlemenin kanıtlamadığı durumlar
Bu kısım genellikle olduğundan fazla abartıldığı için sınırları konusunda kesin olmak gerekir.
- Kaynak kodun güvenli olduğunu söylemez. Açık kaynak koduna eklenen bir arka kapı (backdoor) tekrarlanabilir şekilde derlenir ve tüm yeniden derleyiciler aynı kötü niyetli kaynağı derledikleri için bunu doğrular. Tekrarlanabilirlik, odak noktasını kaynak ağacına kaydırır. O ağacın birileri tarafından incelenmesi gerekir; bu nedenle açık kaynak projelerinde yapay zeka destekli kod politikaları dahil olmak üzere inceleme politikaları ayrı bir kontrol mekanizması olarak kalmaya devam eder.
- Girdilerinizin güvenli olduğunu söylemez. Bağımlılıklar, derlediğiniz şeyin bir parçasıdır. Derleme sırasında çözümlenen kötü niyetli bir paket, oluşturulan yapıya (artifact) dahil edilir ve aynı bağımlılığı çözümleyen her yeniden derleyici sizinle aynı sonucu verir. Bir npm tedarik zinciri saldırısının sunucuya ulaşması bu şekilde gerçekleşir ve tekrarlanabilir bir derleme bunu sadakatle yeniden üretir.
- Araç zincirinin (toolchain) dürüst olduğunu söylemez. Eğer derleyici (compiler) ele geçirilmişse, o derleyiciyi kullanan her yeniden derleyici aynı bozulmuş çıktıyı üretir ve tüm doğrulama sonuçları birbiriyle uyumlu olur. Tekrarlanabilirlik, bu tür bir saldırının maliyetini artırır ancak saldırıyı tespit etmez.
- Güvenlik açıkları hakkında hiçbir şey söylemez. Bit düzeyinde yeniden üretilmiş eski bir kütüphane, yayınlanmış kusurlarıyla birlikte hala eski bir kütüphanedir; bu nedenle sunucunuzu bilinen CVE'ler için tarama işlemini kendi takviminize göre yapmaya devam edin.
Tekrarlanabilirliğin ortadan kaldırdığı şey, belirli bir saldırgan konumudur: derleme makinesi ve kaynak koddan ikili dosyaya (binary) giden yolun tamamı. Bir paket tekrarlanabilir hale gelene kadar, yayıncı dışındaki hiç kimse bu yolu denetleyemez.
Aynı kaynak neden farklı baytlar üretir
Çoğu yazılım varsayılan olarak yeniden üretilebilir değildir ve bunun nedenleri oldukça basittir. Derleyiciler ve arşiv formatları, onları çalıştıran makineye dair verileri kaydeder.
- Zaman damgası.
tar,arvezipformatları dosya değiştirme zamanlarını saklar; bu nedenle farklı bir saniyede derleme yapmak çıktı dosyasını değiştirir. - Yol bilgisi. Hata ayıklama bilgileri mutlak derleme dizinini kaydeder; bu nedenle kod aynı olsa bile
/home/alice/srciçindeki bir derleme ile/build/pkgiçindeki bir derleme birbirinden farklı olur. - Sıralama. Bir dizini okumak, girdileri dosya sistemi sırasına göre döndürür; bu nedenle bağlantı satırı veya arşiv üye sırası makineler arasında değişebilir.
- Kimlik bilgisi. Derleme betikleri, derlemeyi yapan kişinin kullanıcı adını, ana makine adını veya yerel ayarlarını gömer.
- Derleme zamanında alınan kararlar. CPU özelliklerini algılamak veya rastgele bir değer üretmek, çıktının kaynaktan ziyade makineye bağlı olmasına neden olur.
İlk nedenin gerçekleştiğini yaklaşık on saniye içinde gözlemleyebilirsiniz:
mkdir -p /tmp/rb && cd /tmp/rb
echo hello > a.txt && tar cf one.tar a.txt
sleep 2
echo hello > a.txt && tar cf two.tar a.txt
sha256sum one.tar two.tarİki hash değerinin farklı olmasının nedeni, tar başlığının a.txt dosyasının değiştirilme zamanını saklaması ve dosyayı yeniden yazmanın bu zamanı iki saniye ileri taşımasıdır. İçerik, bayt bazında tamamen aynıdır. Meta verileri sabitlemek bu sorunu çözer:
export SOURCE_DATE_EPOCH=1700000000
tar --sort=name --mtime="@${SOURCE_DATE_EPOCH}" --owner=0 --group=0 --numeric-owner -cf three.tar a.txt
touch a.txt
tar --sort=name --mtime="@${SOURCE_DATE_EPOCH}" --owner=0 --group=0 --numeric-owner -cf four.tar a.txt
sha256sum three.tar four.tarArtık hash değerleri eşleşiyor, çünkü arşiv başlığındaki hiçbir alan makinenin o anki durumundan gelmiyor. --sort=name sıralamayı, --mtime ise saati sabitler; sahiplik bayrakları ise kullanıcı kimliğinizin kaydedilmesini engeller.
diffoscope ile farkları okuma
İki derleme birbirinden farklı olduğunda, sha256sum size yalnızca farklı olduklarını söyler, başka bir bilgi vermez. diffoscope, neden farklı olduklarını bir insanın okuyabileceği biçimde açıklamak için geliştirilmiştir. Her iki tarafı özyinelemeli olarak açar, ikili biçimleri metne dönüştürür ve metinleri karşılaştırır. Debian paketlerini, ELF ikili dosyalarını, tar ve ZIP arşivlerini, PDF'leri, SQLite veritabanlarını ve düzinelerce başka biçimi işleyebilir.
sudo apt install -y diffoscope
diffoscope one.tar two.tarYukarıdaki tar çifti için rapor kısadır. Kırpılmış hali şu şekildedir:
--- one.tar
+++ two.tar
├── file list
│ @@ -1 +1 @@
│ -rw-r--r-- 0/0 6 2026-08-18 10:14:02.000000 a.txt
│ +rw-r--r-- 0/0 6 2026-08-18 10:14:04.000000 a.txtTüm teşhis budur: aynı boyut, aynı yol, aynı izinler, farklı değiştirilme zamanı. Gerçek bir paket çok daha uzun bir rapor üretir, bu nedenle raporu bir dosyaya yazdırıp tarayıcıda açın:
diffoscope --html report.html build1.changes build2.changesdiffoscope, girdiler aynı olduğunda 0, farklı olduğunda 1 ve bir sorunla karşılaştığında 2 çıkış kodu döndürür; bu sayede herhangi bir sarmalayıcı betiğe ihtiyaç duymadan doğrudan bir CI işine entegre edilebilir. Küçük bir VPS üzerinde, diffoscope yerine diffoscope-minimal paketini kurun: tam paket, muhtemelen hiçbir zaman kullanmayacağınız çok sayıda biçim yardımcısını da beraberinde getirir.
SOURCE_DATE_EPOCH neyi düzeltir ve nerede durur
SOURCE_DATE_EPOCH, tek bir sayı tutan bir ortam değişkenidir: kaynağın son değiştirilme zamanı, 1 Ocak 1970 UTC'den itibaren geçen saniye cinsinden hesaplanır. Bunu destekleyen bir derleme aracı, işletim sistemine o anki zamanı sormak yerine bu sayıyı kullanır. Değeri derlemeden ziyade kaynağa bağlı kılmak için bunu sürüm kontrol sisteminden ayarlayın:
export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)Debian paketlerinde debhelper, bunu changelog dosyasından sizin için dışa aktarır. debian/rules içerisinde manuel olarak ayarlamak şu şekilde görünür:
export SOURCE_DATE_EPOCH ?= $(shell dpkg-parsechangelog -STimestamp)Destek araca özeldir, genel değildir. cmake 3.8 ve üzeri, gcc 7 ve üzeri, rpm 4.13'ten yüksek sürümler ve Docker buildx 0.10 ve üzeri sürümlerin tümü bunu okur. Kendi betikleriniz, siz kodlamadığınız sürece bunu okumaz. Eğer bir betik date çağırıyorsa, değişkeni ona şu şekilde iletin:
BUILD_DATE="$(date --utc --date="@${SOURCE_DATE_EPOCH:-$(date +%s)}" +%Y-%m-%d)"Uygulama aşamasında tek bir kural önemlidir. Eğer değişken zaten ayarlanmışsa, derlemeniz için o değer o anki zamandır; bu yüzden saati okuyacağınız her yerde bu değeri kullanın ve derlemenizin başlattığı her süreç için değişkenin ayarlı kalmasını sağlayın. Teknik şartname bunu açıkça belirtir: "Derleme süreçleri, eğer zaten mevcutsa, alt süreçler için bu değişkeni kaldırmamalıdır (unset)."
Container imajları aynı sorunu farklı bir sarmalayıcı içinde yaşar. Docker buildx 0.10 ve üzeri sürümler, SOURCE_DATE_EPOCH değişkenini shell ortamınızdan derleme argümanı olarak derlemeye aktarır. Katmanların içindeki dosyalarda bulunan zaman damgalarının dışa aktarıcı tarafından yeniden yazılması gerekir; BuildKit bunu 0.13 sürümünde eklemiştir ve belgelenen yöntem sonucu bir registry adresine gönderir:
export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
docker buildx build --output type=image,name=registry.example.com/app:1.0,push=true,rewrite-timestamp=true .reprotest ile kendi derlemenizi test etme
reprotest, aynı kaynak kodunu iki kez derler, bu iki derleme arasında ortamı kasıtlı olarak değiştirir ve ardından sonuçları karşılaştırır. Varyasyonlar, bu sürecin temel amacıdır. Varsayılan olarak ortam değişkenlerini, derleme yolunu, çekirdek sürümünü, adres alanı rastgeleleştirmesini, CPU sayısını, zamanı, kullanıcı ve grubu, dosya sıralamasını, alan adı ve ana makine adını, ev dizinini, yerel ayarları, çalıştırılabilir dosya yolunu, saat dilimini ve umask değerini değiştirir.
sudo apt install -y reprotest
reprotest . -- null-- ifadesinden sonra gelen her şey derleme ortamı arka ucunu seçer; null ise üzerinde çalıştığınız sistemi ifade eder. Geçici dizinleri incelemek üzere tutmak için reprotest . -vv -- null -d örneğinde olduğu gibi -vv -d parametresini ekleyin. Kaynak ağacının türünü otomatik olarak algılamasını sağlamak için reprotest auto -- null kullanın.
Bazı varyasyonlar ayrıcalık veya ek paketler gerektirir ve çalıştırılamadıklarında belirgin hata mesajları verirler. Tüm süreci root yetkileriyle çalıştırmak yerine, bu varyasyonları devre dışı bırakın:
reprotest --vary=-user_group,-domain_host,-fileordering auto -- nullreprotest tarafından bildirilen her sorun, bir yeniden derleyicinin (rebuilder) daha sonra projenizin adını kullanarak herkese açık bir şekilde raporlayacağı bir sorundur.
Yeniden oluşturucu (rebuilder) kararı ne anlama gelir
Yeniden oluşturucu, yayıncının makinesi olmayan bir makinedir. Yayınlanan kaynağı ve kaydedilmiş derleme ortamını alır, paketi tekrar derler ve kendi çıktısını arşivdeki yapıtla karşılaştırır. Karar, yalnızca makine bağımsız olduğu için bir değer taşır.
Debian, ortamı .buildinfo dosyasında kaydeder ve dpkg-buildpackage bunu .deb dosyasının yanına yazar. Alanlar işin ilginç kısmıdır. Installed-Build-Depends, derlemeyi etkileyebilecek her yüklü paketi tam sürümleriyle listeler. Build-Path, derlemenin nerede çalıştığını kaydeder. Environment, önemli olduğu bilinen ortam değişkenlerini kaydeder. Checksums-Sha256 ise çıktıları kapsar. Bu dosya, ikinci bir deneme için reçetedir:
sudo apt install -y devscripts mmdebstrap
debrebuild --buildresult=./artifacts --builder=mmdebstrap hello_2.10-2_amd64.buildinfodebrebuild, buildinfo dosyasını okur ve içinde belirtilen tam bağımlılık sürümlerini snapshot.debian.org üzerinden çeker; böylece bugün yapılan bir yeniden derleme, orijinal derlemenin yapıldığı gün mevcut olan paket sürümlerini kullanabilir. mmdebstrap derleyicisi, chroot kurulumuna veya süper kullanıcı haklarına ihtiyaç duymaz. Ürettiği yapıtları, diffoscope kullanarak arşiv kopyasıyla karşılaştırın.
Arch Linux, bunu sürekli olarak yapan ve kararları yayınlayan rebuilderd aracını çalıştırır:
rebuildctl -H https://reproducible.archlinux.org pkgs ls --name rebuilderdDurumlar GOOD, BAD ve UNKWN şeklindedir ve her birinin düz okuması farklı bir yönde yanlıştır. GOOD, bağımsız bir tarafın aynı baytları elde ettiği anlamına gelir; bu, derleme hakkında güçlü bir ifadeyken, kaynak hakkında hiçbir şey ifade etmez. BAD neredeyse hiçbir zaman bir saldırı değildir: yaygın neden, paketlemenin sabitlemeyi unuttuğu bir zaman damgası veya yoldur; bu yüzden rebuilderd, hataya bir diffoscope raporu ekleyebilir. UNKWN, kimsenin test etmediği anlamına gelir ve test edilmemiş bir paket, onaylanmış bir paket değildir.
Bu nedenle operasyonel kural kısadır. BAD kararı, raporu okumak için bir nedendir. Rapor zaman damgalarını, derleme yollarını veya üye sıralamasını gösteriyorsa, bir paketleme hatası kaydı açın. Eğer bu tür bir açıklama olmaksızın farklı yürütülebilir kod gösteriyorsa, o derlemeyi dağıtmayı durdurun ve durumu üst birimlere bildirin.
Debian şu anda ne kadar tekrarlanabilir?
Bu grafiğin arkasındaki veriler
[
{
"label": "unstable",
"percent_reproducible": 94.8,
"tested_count": "41,490"
},
{
"label": "forky",
"percent_reproducible": 95.7,
"tested_count": "39,529"
},
{
"label": "experimental",
"percent_reproducible": 72.5,
"tested_count": "619"
}
]2026-10-15 tarihi itibarıyla, amd64 mimarisindeki unstable sürümü, test edilen 41,490 paket genelinde 94.8% oranında tekrarlanabilirlik seviyesindedir. Experimental sürümü ise, henüz düzeltme çalışmaları tamamlanmamış paketlerden oluşan çok daha küçük ve yeni bir örneklem kümesi olan 619 paket üzerinden 72.5% seviyesinde kalmıştır.
Bu rakamlar, 2026-10-15 tarihinde tests.reproducible-builds.org adresindeki Debian sayfasından alınmıştır; ilgili sayfa "Last update: 2026-10-15 13:02 UTC" damgasını taşımaktadır. Bu veriler değişkenlik gösterir. Altı ay sonra bu paragraftan alıntı yapmak yerine takip sistemini kontrol edin.
Yüzde oranından daha önemli bir uyarı bulunmaktadır. Bu çerçeve, her paketi kendi donanımı üzerinde iki kez derler, iki derleme arasında ortam değişkenlerini değiştirir ve elde edilen iki sonucu karşılaştırır. Bu işlem, bir paketin tekrarlanabilir şekilde derlenip derlenemeyeceğini ölçer. Bu, arşivde bulunan .deb dosyasının yayınlanan yapıtla eşleşip eşleşmediğini denetleyen bir mekanizma değildir; bu, yayınlanmış yapıtla karşılaştırma yapan bir yeniden derleyicinin (rebuilder) ayrı bir görevidir. Her iki veri de faydalıdır. Farklı sorulara yanıt verirler; ancak insanlar genellikle ilkini ikincisiymiş gibi alıntılamaktadır.
Kendi sunucunuzda yapmanız gerekenler
Bir dağıtımı yeniden oluşturmayacaksınız. Sıradan bir sunucuya aktarılan parçalar daha küçük ve ucuzdur.
- Araç zincirini (toolchain) sabitleyin. Etiketle referans verilen bir temel imaj, siz fark etmeden değişebilir. İmajı digest değeriyle referans alın ve bu değeri sürüm kaydına ekleyin.
- Girdileri kaydedin. Lockfile dosyasını, imaj digest değerini ve derleyici sürümünü artifact ile birlikte saklayın. Ortamını yeniden oluşturamadığınız bir yapı (build) tekrar derlenemez, dolayısıyla asla denetlenemez.
- CI sürecinde iki kez derleme yapın ve çıktılar farklı olduğunda işi başarısız sayın. Bu, bir ekstra derleme maliyetiyle, belirsizliği (nondeterminism) bir yıl sonra bir olay anında değil, sisteme girdiği gün yakalamanızı sağlar.
- Derleyicinin gömdüğü yolları temizleyin. Go için
go build -trimpath -buildvcs=falsederleme dizinini ve sürüm kontrol damgasını kaldırır,go version -m ./appise ikili dosyada (binary) gerçekte neyin kaldığını yazdırır. - Dağıttığınız şeyin hash değerini saklayın. Çalışan ikili dosyanın bir kaynak revizyonuna karşılık gelip gelmediğini bilmeniz gerektiğinde, bu kayıt yanıt verebilecek tek şeydir.
CI kontrolü dört satırdan oluşur:
set -eu
./build.sh && mv dist/app app.1
./build.sh && mv dist/app app.2
diffoscope --text - app.1 app.2diffoscope, iki artifact birbirinden farklı olduğunda sıfır olmayan bir çıkış kodu döndürür; böylece iş kendiliğinden başarısız olur ve günlük kayıtlarında okunabilir bir açıklama bırakır. Tüm fikir, tek bir depoya indirgenmiş olarak budur: bir ikili dosyanın bir kaynak ağacından geldiği iddiası, ikinci bir makinenin doğrulayabileceği bir şey olmalıdır.
FAQ
Yeniden üretilebilir (reproducible) bir derleme, yazılımın güvenli olduğu anlamına mı gelir?
Hayır. Bu durum, yalnızca ikili dosyanın kaynak kodla eşleştiğini kanıtlar, başka bir şey değil. Genel kaynak ağacına işlenmiş bir arka kapı (backdoor) yeniden üretilebilir şekilde derlenir ve her yeniden derleyici, aynı kötü niyetli kaynağı derlediği için bunu doğrular. Bilinen bir CVE içeren bir paket mükemmel şekilde yeniden üretilir ve savunmasız kalmaya devam eder. Yeniden üretilebilirlik, saldırganın elindeki bir avantajı, yani derleme makinesini ve kaynak koddan ikili dosyaya giden yolu ortadan kaldırır. Kaynak kodu okumak ve güvenlik açıklarını takip etmek, yeniden üretilebilirliğin sizin yerinize yapmadığı ayrı işlerdir.
Kaynak kodda hiçbir şey değişmediği halde neden iki derlemem birbirinden farklı?
Neredeyse her zaman bir zaman damgası, bir yol veya bir sıralama farkından kaynaklanır. tar ve zip gibi arşiv formatları dosya değiştirme zamanlarını saklar; bu nedenle farklı bir saniyede yapılan checkout işlemi farklı baytlar üretir. Hata ayıklama bilgileri mutlak derleme dizinini kaydeder, bu yüzden /home/alice/src ve /build/pkg aynı koddan farklı ikili dosyalar üretir. Dizin okuma işlemleri, dosya sistemi sırasına göre girdi döndürür; bu nedenle bir bağlantı satırındaki nesne dosyaları başka bir makinede farklı şekilde sıralanabilir. diffoscope build1 build2 komutunu çalıştırın; rapor, tahmin yürütmenize gerek kalmadan sorunun hangisi olduğunu size söyleyecektir.
SOURCE_DATE_EPOCH nedir ve bunu ayarlamam gerekir mi?
Bu, tek bir sayı tutan standart bir ortam değişkenidir: kaynağın son değiştirilme zamanı, 1 Ocak 1970 UTC'den itibaren geçen saniye cinsinden. Bunu dikkate alan araçlar, aksi takdirde sistem saatini okuyacakları her yerde bu değeri kullanırlar. Bunu sürüm kontrol sisteminden export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) ile ayarlayın. Otomatik değildir ve genel bir çözüm değildir. Yalnızca bunu uygulayan araçlar bu değeri okur ve kendi derleme betikleriniz de bunu kendileri okumalıdır; bu nedenle date komutunu çağıran bir betik, siz değiştirene kadar geçerli zamanı damgalamaya devam eder.
Bir yeniden derleyici (rebuilder) BAD raporu verdiğinde ne yapmalıyım?
Başka bir şey yapmadan önce raporu okuyun. BAD kararı, bağımsız bir yeniden derlemenin aynı baytları üretmediği anlamına gelir ve bunun olağan nedeni bir saldırıdan ziyade paketlemedeki deterministik olmayan durumlardır. rebuilderd tam da bu nedenle bir diffoscope raporu oluşturabilir. Eğer farklar zaman damgaları, derleme yolları veya dosya sıralaması ise, bu bildirilmesi gereken bir paketleme hatasıdır. Eğer fark, bu tür bir açıklaması olmayan yürütülebilir kod ise, o derlemeyi dağıtmayı durdurun, yapıları (artifacts) saklayın ve durumu yayıncıya bildirin.