sops-nix: NixOS'ta Şifreli Secret Yönetimi
NixOS’un güçlü yanı, tüm sistem konfigürasyonunu tek bir Git repo’sunda tutabilmektir. Ama bu hemen bir soru doğurur: peki ya şifreler?
Bir self-hosted servis kurduğunuzda — örneğin bir RSS okuyucu, bir veritabanı, bir API servisi — admin şifresinin bir yerde tanımlı olması gerekir. NixOS konfigürasyonuna yazarsanız Git geçmişine girer. Git geçmişine giren bir şey, repo public olsa olmasa, bir kez commit edildiğinde pratik olarak sonsuza dek oradadır.
sops-nix bu sorunu çözüyor: secret’ları şifreli halde Git’e atmanızı, şifre çözme işlemini ise boot sırasında otomatik yapmanızı sağlıyor.
Sops ve Age Nedir?
sops (Secrets OPerationS), Mozilla tarafından geliştirilen bir CLI aracı. YAML, JSON, ENV dosyalarını şifreleyip düzenlemenizi sağlıyor. Şifreleme backend olarak GPG, age veya bulut KMS sistemlerini destekliyor.
age, GPG’ye modern ve sade bir alternatif. GPG’nin karmaşık anahtar yönetimi yerine çok daha basit bir format sunuyor. Önemli bir özelliği: mevcut SSH ed25519 anahtarınızdan otomatik olarak age anahtarı türetilebiliyor. Yani yeni bir anahtar çifti oluşturmanıza gerek yok.
sops-nix ise bu ikisini NixOS’a entegre eden topluluk modülü: şifreli dosyaları boot sırasında çözüp servislerin kullanabileceği formata getiriyor.
Kurulum Mantığı
Üç parça var:
-
Özel anahtar — Şifre çözme için. Bende SSH ed25519 private key,
/persist/kullanici/.ssh/id_ed25519yolunda./persist’te olmasının nedeni boot sırası: şifre çözme,/homemount edilmeden önce gerçekleşiyor. -
Public key — Şifreleme için. SSH public key’den
ssh-to-agekomutuyla türetiliyor:nix run nixpkgs#ssh-to-age -- -i ~/.ssh/id_ed25519.pub # age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -
Şifreli dosya —
secrets/secrets.yaml. Cleartext asla yok; Git’e sadece şifreli hali gidiyor.
Konfigürasyon Dosyaları
.sops.yaml — Kim Şifreleyebilir?
Repo kökünde .sops.yaml dosyası, SOPS’a hangi anahtarlarla hangi dosyaları şifreleyeceğini söylüyor:
keys:
- &kullanici age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
creation_rules:
- path_regex: secrets/secrets\.yaml$
key_groups:
- age:
- *kullanici
Bu dosya sayesinde sops secrets/secrets.yaml komutu hangi public key’le şifreleyeceğini biliyor. Birden fazla anahtar tanımlanırsa (örneğin birden fazla makine), tüm anahtarlar dosyayı açabilir.
secrets/secrets.yaml — Şifreli İçerik
Dosyanın içeriği şöyle görünüyor:
miniflux_admin_password: ENC[AES256_GCM,data:cP8iLiOh...,iv:R+UsWV/...,tag:L8eJBodp...,type:str]
searxng_key: ENC[AES256_GCM,data:4hCw4Xmw...,iv:2QO+z04J...,tag:NmJ1Xfwp...,type:str]
sops:
age:
- recipient: age1xxxxx...
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
...
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-05-30T13:42:09Z"
version: 3.12.1
Her değer ENC[AES256_GCM,...] formatında şifreli. Dosya Git’te açıkça duruyor, ama içeriği okumak için private key gerekiyor. sops secrets/secrets.yaml komutu dosyayı otomatik çözüp düzenleyebilir hale getiriyor, kaydedince tekrar şifreliyor.
modules/core/sops.nix — NixOS Entegrasyonu
sops = {
defaultSopsFile = ../../secrets/secrets.yaml;
validateSopsFiles = false;
age.sshKeyPaths = [ "/persist/kullanici/.ssh/id_ed25519" ];
};
age.sshKeyPaths ile sops-nix’e “bu SSH key’i age formatına çevir ve şifre çözme için kullan” diyorsunuz. Ayrıca bir age anahtarı dosyası tanımlamanıza gerek yok — SSH anahtarı yeterli.
validateSopsFiles = false ilk kurulum aşamasında işe yarıyor: modül aktif ama henüz hiç secret tanımlanmamışsa hata vermesini engelliyor.
Secret’ı Servise Vermek
Sadece şifreli dosyayı okumak yetmez; o verinin bir servise ulaşması gerekiyor. sops-nix’in iki mekanizması var:
sops.secrets — Tekil Değer
sops.secrets.miniflux_admin_password = {
owner = "root";
};
Bu tanım, boot sırasında miniflux_admin_password değerini secrets.yaml’dan çözüp /run/secrets/miniflux_admin_password yoluna yazıyor. Dosya root’a ait, sadece root okuyabilir. /run/secrets/ tmpfs üzerinde — disk’e yazılmıyor, her boot’ta yeniden oluşturuluyor.
sops.templates — Birden Fazla Secret’tan Dosya Oluşturma
Bazı servisler birden fazla değeri tek bir env dosyasında istiyor. Miniflux bunlardan biri — hem kullanıcı adı hem şifre aynı dosyada olmalı:
sops.templates."miniflux-admin.env" = {
content = ''
ADMIN_USERNAME=kullanici
ADMIN_PASSWORD=${config.sops.placeholder.miniflux_admin_password}
'';
owner = "root";
};
services.miniflux = {
adminCredentialsFile = config.sops.templates."miniflux-admin.env".path;
};
config.sops.placeholder.xxx sözdizimi, derleme sırasında secrets.yaml’a dokunmadan bir yer tutucu bırakıyor. Boot sırasında sops-nix bu placeholder’ı gerçek değerle değiştiriyor ve dosyayı /run/secrets.d/ altına yazıyor.
Boot Sırası
Şifre çözme süreci şöyle işliyor:
- Boot başlar,
/persistmount edilir (neededForBoot = true) sops-install-secretssystemd servisi çalışır/persist/kullanici/.ssh/id_ed25519ilesecrets.yamlçözülür- Çözülmüş değerler
/run/secrets/altına yazılır (tmpfs) - Diğer servisler (Miniflux, SearXNG) başlar ve
/run/secrets/dosyalarını okur
Bu sıra önemli: /home bu aşamada henüz mount edilmemiş. Anahtar /home/kullanici/.ssh/ yerine /persist/kullanici/.ssh/ altında olmasının nedeni bu.
Sonuç
Git repo’nuzda plaintext şifre olmadan çalışan bir yapı:
secrets.yamlşifreli, Git’e gidiyor- Şifre çözme anahtarı sadece makinede,
/persist’te - Servisler
/run/secrets/üzerinden runtime’da şifreye ulaşıyor - Disk’e hiçbir zaman cleartext yazılmıyor
NixOS’un deklaratif yapısı burada iyi oturuyor: neyin hangi servise gideceği konfigürasyonda açıkça tanımlı, elle müdahale yok.
İlgili: Impermanence: NixOS’ta Her Boot’ta Sıfırlanan Bir Root