SSD Nodes Learn 🎉 VPS $5.50/aydan başlayan
Rehberler Matt ConnorYazan Matt Connor

Ansible Vault ile Git uzerinde gizli verileri sifreleme

Ansible Vault kullanarak playbook dosyalarindaki parola ve API anahtarlarini nasil guvenli tutacaginizi ogrenin. Dosya bazli sifreleme ve tekil degisken korumasi hakkinda detaylar.

Ansible Vault neyi korur, neyi korumaz

Ansible Vault, playbook deponuzdaki gizli verileri şifreler; böylece git, düz metin halindeki parolalar yerine şifreli metinleri saklar. ansible-vault komutu, seçtiğiniz bir paroladan türetilen simetrik anahtarı kullanarak ya tüm dosyayı ya da dosya içindeki tek bir değeri şifreler. Ansible, play çalıştırıldığında bu içeriği bellekte şifresini çözerek kullanır; böylece değişken, diğer tüm değişkenler gibi davranır.

Bu modelin net bir sınırı vardır. Vault, bir gizli veriyi depo içinde hareketsiz haldeyken korur, fazlasını değil. Bir görev çalıştığı anda değer; bellekte, oluşturulan şablonda, modül argümanlarında ve siz engellemediğiniz sürece çalışma çıktısında düz metin halindedir. Playbook'u çalıştırabilen herkes vault parolasına sahiptir; bu nedenle vault, ekip dışındaki kişilere karşı gizlilik sağlar, ekip içinde kişiye özel erişim denetimi sağlamaz.

Henüz bir playbook yazmadıysanız, bir VPS üzerinde ilk Ansible playbook'u ile başlayın ve playbook'unuzun bir parolaya ihtiyaç duyduğu aşamada buraya geri dönün.

Tüm dosyayı mı yoksa tek bir dizgeyi mi şifrelemeli?

ansible-vault encrypt, bir dosyanın içeriğini şifreli metin ile değiştirir. Dosya, $ANSIBLE_VAULT ile başlayan bir başlık satırı altında tek bir base64 metin bloğuna dönüşür. Dosya yalnızca gizli veriler içerdiğinde bu yöntemi kullanın.

ansible-vault encrypt_string, tek bir değeri şifreler ve normal bir vars dosyasına yapıştırabileceğiniz bir YAML parçası çıktısı verir. Değişken adı okunabilir kalır ve yalnızca değer şifreli metin olur. Gizli veriler düz metin ayarların yanında yer aldığında bu yöntemi kullanın.

Günlük iş akışında önemli olan fark, diff çıktısıdır. Bir vault dosyası her kaydedildiğinde yeni ve rastgele bir tuz (salt) değeriyle yeniden şifrelenir; bu nedenle şifreli metnin her baytı değişir. git diff bu durumda okunamaz bir bloğun başka bir okunamaz blokla değiştirildiğini gösterir; bu da bir inceleme yapan kişinin tek bir parolayı mı güncellediğinizi yoksa dosyayı tamamen mi yeniden yazdığınızı anlamasını imkansız kılar. encrypt_string ile her gizli veri, düz metin bir dosya içinde kendi bloğuna sahip olur; böylece bir diff çıktısı tam olarak hangi değişkenin değiştiğini gösterir ve dosyanın geri kalanına dokunmaz.

Satır içi (inline) biçimin bir maliyeti vardır ve bu maliyet rotasyon zamanında ortaya çıkar: ansible-vault rekey satır içi bloklara müdahale etmez. Gizli veri listesi uzunsa ve nadiren değişiyorsa dosya biçimini seçin. Gizli veriler normal değişkenlerle karışık durumdaysa ve kod incelemesinin bir anlam ifade etmesini istiyorsanız satır içi biçimi seçin.

Korunan verileri gösteren group_vars düzeni

Ansible, group_vars/<group>.yml dosyasını yükler ve ayrıca group_vars/<group>/ dizini içindeki her dosyayı da yükler. Dizin yapısı tercih edilmelidir; çünkü bu yapı, tek bir grubun düz metin bir dosya ile şifreli bir dosyayı yan yana taşımasına olanak tanır.

inventory/
  hosts.ini
group_vars/
  all/
    vars.yml
    vault.yml
  web/
    vars.yml
    vault.yml
host_vars/
  db01/
    vars.yml
    vault.yml
playbooks/
  site.yml

Her vault.yml şifrelidir. Her vars.yml ise düz metindir. Dosya adı durumu belirttiği için, bir okuyucu herhangi bir dosyayı açmadan hangi değerlerin korunduğunu görebilir.

Bu modelin ikinci yarısı dolaylı yönlendirmedir. Şifreli dosyanın içinde her değişkenin başına vault_ ön ekini getirin.

vault_db_password: "a real password"
vault_grafana_admin_token: "a real token"

Ardından, yanındaki düz metin dosyasından bu isimlere referans verin.

db_password: "{{ vault_db_password }}"
grafana_admin_token: "{{ vault_grafana_admin_token }}"

Roller ve şablonlar db_password kullanır ve değerin nereden geldiğini asla öğrenmezler; bu da playbook ile rol arasındaki ayrımı temiz tutar. Düz metin vars.yml, aranabilir bir dizin işlevi görür: grep -r vault_ group_vars/, herhangi bir şifre çözme işlemi yapmadan deponun beklediği her gizli veriyi listeler. Bunun maliyeti, gizli veri başına bir fazladan isimdir ve vault_ ismindeki bir yazım hatası, çalışma zamanında söz dizimi hatası yerine tanımlanmamış bir değişken olarak ortaya çıkar.

encrypt_string ile bir değişkeni şifreleme

ansible-vault encrypt_string --vault-id prod@~/.ansible/vault-prod.txt \
  --stdin-name 'vault_db_password'

Gizli değeri yazın ve ardından Ctrl-D tuşlarına basın. --stdin-name değeri standart girdiden okur; bu sayede değer, shell geçmiş dosyanıza kaydedilmez. Diğer yöntem ise değeri doğrudan komut satırına yazmaktır; ancak bu durumda shell değeri geçmiş dosyasına kaydeder:

ansible-vault encrypt_string --vault-id prod@~/.ansible/vault-prod.txt \
  'a real password' --name 'vault_db_password'

Her iki durumda da komut bir YAML bloğu çıktısı verir. Bu bloğu, yazdırıldığı haliyle vars dosyasına yapıştırın; çünkü !vault etiketinin altındaki girintileme, değerin bir parçasıdır.

vault_db_password: !vault |
          $ANSIBLE_VAULT;1.2;AES256;prod
          6638643965323633646262656665306333616466396630323136393465356136396436383331
          3131303163306665326539353837343663313762616561306534373963383531613664393332

!vault etiketi, YAML yükleyicisine skaler değerin metin değil şifreli metin olduğunu bildirir. Başlık kısmı; format sürümünü, şifreleme algoritmasını ve veriyi şifreleyen vault ID etiketini içerir. Bir vault ID olmadan şifrelenen değerler, etiketsiz bir 1.1 başlığı taşır. Bu yöntem hala çalışır durumdadır ancak parolanın kaynağı hakkında daha az bilgi sağlar.

Vault parolası nerede tutulur?

Deponun (repository) dışında. Bu, istisnası olmayan tek kuraldır.

--ask-vault-pass her çalıştırmada bir kez parola ister ve hiçbir şeyi saklamaz. Dizüstü bilgisayarlar için uygundur; cron işleri veya CI çalıştırıcıları için uygun değildir.

Parola dosyası, ilk satırı paroladan oluşan düz bir metin dosyasıdır. Dosyayı kısıtlı izinlerle boş olarak oluşturun, ardından bir düzenleyici ile doldurun; böylece parola hiçbir zaman shell geçmişinize düşmez:

mkdir -p ~/.ansible
install -m 600 /dev/null ~/.ansible/vault-prod.txt
$EDITOR ~/.ansible/vault-prod.txt

Herhangi bir komutu --vault-password-file ile bu dosyaya yönlendirin:

ansible-playbook -i inventory/hosts.ini playbooks/site.yml \
  --vault-password-file ~/.ansible/vault-prod.txt

Bu bayrağı her komutta tekrarlamak unutulmaya müsaittir, bu yüzden deponun kök dizinindeki ansible.cfg içerisinde bir kez ayarlayın.

[defaults]
inventory = inventory/hosts.ini
vault_password_file = ~/.ansible/vault-prod.txt

Aynı ayar, bir CI işinin parolayı sağlama yöntemi olan ANSIBLE_VAULT_PASSWORD_FILE ortam değişkeninden de okunur. İş, parolayı kendi kimlik bilgisi deposundan geçici bir dizindeki dosyaya yazar, değişkeni dışa aktarır (export) ve çalışma bittiğinde dosyayı siler. Dosya adı desenini .gitignore içerisine de ekleyin; çünkü ansible.cfg içindeki yol depoya işlenmiştir (commit) ve er ya da geç birisi gerçek dosyayı checkout dizini içinde oluşturacaktır.

Eğer parola dosyası çalıştırılabilir (executable) ise, Ansible dosyayı çalıştırır ve parolayı metin olarak okumak yerine standart çıktısından (stdout) okur. Vault parolasını diske hiç yazmadan bir sistem anahtarlığından veya bulut gizli yönetim servisinden bu şekilde çekersiniz. --vault-id üzerinden kullanılan bir betiğin ek gereksinimleri vardır: adı -client veya -client artı bir uzantı ile bitmelidir, çalıştırılabilir olmalıdır, --vault-id seçeneğini kabul etmelidir ve parolayı standart çıktıya yazdırmalıdır.

İki vault kimliği: staging ve production

Vault kimliği, label@source şeklinde yazılan ve vault parolasına eklenen bir etikettir. Kaynak; bir parola dosyasının yolu olan prompt veya bir istemci betiğinin yoludur. Etiketler, tek bir deponun birden fazla parola altında gizli verileri tutmasına olanak tanır; böylece staging parolası production dosyasını açmaz.

ansible-vault encrypt --vault-id staging@~/.ansible/vault-staging.txt \
  group_vars/staging/vault.yml
ansible-vault encrypt --vault-id prod@~/.ansible/vault-prod.txt \
  group_vars/prod/vault.yml

Bir çalıştırmanın ihtiyaç duyabileceği her kimliği iletin:

ansible-playbook playbooks/site.yml \
  --vault-id staging@~/.ansible/vault-staging.txt \
  --vault-id prod@~/.ansible/vault-prod.txt

Veya bunları ansible.cfg içinde bir kez listeleyin:

[defaults]
vault_identity_list = staging@~/.ansible/vault-staging.txt, prod@~/.ansible/vault-prod.txt

Bir davranış kullanıcıları şaşırtır. Varsayılan olarak etiket bir kilit değil, bir ipucudur. Ansible, dosyanın şifresini çözene kadar elindeki her gizli veriyi dosyaya karşı dener; bu nedenle staging etiketli bir dosya, eğer production parolası doğru anahtar ise yine de açılır. [defaults] altında vault_id_match = True ayarını yapın veya ANSIBLE_VAULT_ID_MATCH ortam değişkenini kullanın; böylece Ansible yalnızca etiketi dosya başlığıyla eşleşen gizli veriyi kullanır. Bu denetim 1.2 başlığını gerektirir, bu nedenle yalnızca en başta bir vault kimliği ile şifrelenmiş içeriklere uygulanır.

Birden fazla kimlik yüklendiğinde, ansible-vault encrypt artık hangi parola ile şifreleme yapacağını bilemez. --encrypt-vault-id prod ile bir isim belirtin veya deponun bir varsayılanı olması için ansible.cfg içinde vault_encrypt_identity ayarını yapın.

Bunun getirisi dağıtım kapsamıdır. Staging dağıtımı yapan bir CI işine yalnızca staging parolası verilir; böylece güvenliği ihlal edilmiş bir çalıştırıcı (runner), production kimlik bilgilerini okuyamaz. Tek bir kontrol makinesinden bir Linux sunucu filosuna play'leri çalıştırdığınızda, bu ayrım küçük bir olay ile çok büyük bir olay arasındaki farkı belirler.

Bir çalışan ayrıldığında vault anahtarını yenileme

Anahtar yenileme (rekey), vault parolasını değiştirir ve içeriği yeni parola ile yeniden şifreler. Bu işlem geçmişe dönük bir geri alma sağlamaz. Eski parolaya sahip olan herkes, elinde tuttuğu depo kopyasını ve bu kopyadaki tüm eski commit'leri şifrelemeye devam edebilir. Bu nedenle, bir kullanıcı ayrıldığı anda vault parolasını geçersiz kabul edin ve rotasyonu şu sırayla gerçekleştirin:

  1. Sunuculardaki ve üçüncü taraf servislerdeki gerçek kimlik bilgilerini değiştirin. Erişimi fiilen iptal eden adım budur.
  2. Yeni değerleri ansible-vault edit ile vault dosyalarına girin.
  3. Şifrelenmiş her dosyayı yeni bir vault parolası ile yeniden anahtarlayın (rekey).
  4. Yeni vault parolasını, depodan bağımsız bir kanal üzerinden, erişim yetkisi devam eden kişilere iletin.
ansible-vault rekey --vault-id prod@~/.ansible/vault-prod-old.txt \
  --new-vault-id prod@prompt \
  group_vars/prod/vault.yml host_vars/db01/vault.yml

rekey komutu tek seferde birden fazla dosyayı kabul eder ve --new-vault-id prod@prompt komutu parolayı diskten okumak yerine tek seferde sorar. Değiştirmek için geçerli bir nedeniniz yoksa etiketi (label) aynı tutun; çünkü etiket, komutun yeniden yazdığı her dosyanın başlığına işlenir.

Satır içi (inline) formun maliyeti burada ortaya çıkar. ansible-vault rekey tamamen şifrelenmiş dosyalar üzerinde çalışır, bu nedenle düz metin bir vars dosyası içinde yer alan !vault bloğu olduğu gibi kalır. Bunları önce tespit edin, ardından her birini yeni parola ile encrypt_string kullanarak yeniden oluşturun:

grep -rl '!vault' group_vars/ host_vars/

Takasın tamamı budur. Satır içi bloklar okunabilir farklar (diff) sunar ancak rotasyon sırasında manuel bir işlem gerektirir. Tamamen şifrelenmiş dosyalar ise tek komutla rotasyona girer ancak inceleme sırasında herhangi bir anlamlı veri sunmaz.

Gizli verinin çıktınızda görünmeye devam etme nedeni

Vault, değerin şifresi çözüldüğü anda görevini tamamlar. Ansible bir görevin sonucunu raporlar ve argümanlarını ekrana yansıtan bir modül, kimlik bilgisini bu rapora taşır. Ayrıntılı (verbose) bir çalıştırma, bir şablon görevindeki --diff, argümanlarını döken başarısız bir görev veya çıktıları bir dosyaya yazan bir geri çağırma (callback) eklentisi, her biri düz metni tutacaktır. Dosyayı şifrelemek bunların hiçbiri için bir çözüm sağlamaz.

no_log: true anahtardır. Kimlik bilgisi alan her görevde bu ayarı kullanın.

- name: Write the application environment file
  ansible.builtin.template:
    src: app.env.j2
    dest: /etc/myapp/app.env
    owner: myapp
    group: myapp
    mode: "0600"
  no_log: true

Ansible, bu durumda görevin sonucunu çıktıdan gizler; böylece günlük kayıtları, görevin neyi işlediğini kaydetmeden çalıştığını doğrular. Özellikle döngülerde bu ayarı kullanın; çünkü bir döngü her öğe için bir sonuç raporlar ve bir kimlik bilgisi listesi üzerindeki döngü, listenin tamamını rapor eder.

Şifresi çözülmüş bir gizli verinin sızdığı ve no_log kapsamına girmeyen diğer dört durum şunlardır:

  • Bir şablondan oluşturulan dosya, ona verdiğiniz mode ve owner değerlerini devralır. Kimlik bilgisi içeren her şeyde mode: "0600" ve belirli bir kullanıcı sahipliği ayarlayın; aksi takdirde gizli veri hedef sunucuda herkes tarafından okunabilir hale gelir.
  • ansible.builtin.command veya ansible.builtin.shell komutlarına aktarılan bir gizli veri, komut çalışırken hedef sunucudaki süreç listesinde görünür ve buradaki herhangi bir yerel kullanıcı tarafından okunabilir. Bunun yerine veriyi bir dosya veya ortam değişkeni aracılığıyla aktarın.
  • Fact caching (olgu önbellekleme), toplanan olguları kontrol makinesindeki diske yazar; bu nedenle gizli veri tutan kayıtlı bir değişken, kimsenin hassas olduğunu düşünmediği bir önbellek dosyasında kalabilir.
  • Aynı gizli veri genellikle bir container tarafından okunan ortam dosyası gibi ikinci bir yerde bulunur. Oradaki kurallar farklıdır ve Compose ortam dosyalarında kimlik bilgilerini gizli tutma konusu bu tarafı ele alır.

no_log hata ayıklamayı zorlaştırır, zaten amacı da budur. Bir görev hatalı çalıştığında test sunucusunda bu ayarı geçici olarak kaldırın ve değişiklik üretime geçmeden önce tekrar ekleyin.

Şifrelenmiş dosyaları düz metin izi bırakmadan okuma ve düzenleme

ansible-vault view group_vars/prod/vault.yml, dosyayı bir pager içine şifresini çözerek aktarır ve diske hiçbir şey yazmaz. ansible-vault edit, dosyayı geçici bir dosyaya şifresini çözerek açar, $EDITOR editörünüzü başlatır ve kapattığınızda dosyayı yeniden şifreler. Her iki yöntemi de, çalışma dizininde düz metin bir dosya bırakan ansible-vault decrypt yöntemine tercih edin. Yanlışlıkla stage edilen şifresi çözülmüş bir vault dosyası, gerçek kimlik bilgilerinin halka açık bir depoya sızmasının en yaygın yoludur.

Git, tamamen şifrelenmiş dosyaları işlem sırasında şifrelerini çözerek okunabilir bir diff çıktısı verebilir:

git config --local diff.ansible-vault.textconv "ansible-vault view --vault-password-file ~/.ansible/vault-prod.txt"
printf '%s\n' 'group_vars/**/vault.yml diff=ansible-vault' >> .gitattributes

Bunu etkinleştirmeden önce ne yaptığını anlayın. git diff artık üretim ortamı sırlarını terminalinize yazdıracaktır; bu da onları ekran geçmişinize ve ekran paylaşımı yaptığınız her yere taşır. Bu, tek bir makinedeki tek bir kullanıcı için yerel bir kolaylıktır; bu nedenle git config ayarını yerel tutun ve başkalarının aynı ayarı yapmadığı sürece kendi çalışma kopyalarında farklı davranacağını göz önünde bulundurun.

Vault'un yetersiz kaldığı durumlar

Vault, her etiket için tek bir parola içeren bir dosya biçimidir ve bu yapı, sınırlarının nerede bittiğini belirler. Aşağıdaki durumlardan herhangi biri geçerli olduğunda gerçek bir gizli veri deposuna (secret store) geçiş yapın.

  • Kişi bazlı erişim yetkilendirmesine ihtiyaç duyduğunuzda. Playbook'u çalıştıran herkes aynı parolaya sahiptir ve Vault ID'leri erişimi ortama göre ayırır, kişiye göre değil.
  • Denetim izine (audit trail) ihtiyaç duyduğunuzda. Vault, neyin kim tarafından ve ne zaman şifresinin çözüldüğüne dair hiçbir kayıt tutmaz.
  • Belirli bir takvime göre parola rotasyonu yapmanız gerektiğinde. Vault'ta son kullanma tarihi veya sürümleme yoktur; bu nedenle bir kimlik bilgisinin iki yıldır değişmediğini size bildiren bir mekanizma bulunmaz.
  • Uygulamanın çalışma zamanında (run time) gizli veriye ihtiyaç duyması durumunda. Başlangıçta veritabanı parolasını okuyan bir servis, bu veriyi dağıtım deponuzdan (deployment repository) okumamalıdır.

Bu noktada yöntem tersine döner. Ansible gizli verileri depolamayı bırakır ve bunları çalışma zamanında bir lookup eklentisi aracılığıyla HashiCorp Vault'tan (kafa karıştırıcı derecede benzer bir isme sahip farklı bir ürün), bir bulut sağlayıcısının gizli veri yöneticisinden veya kontrol makinesindeki bir anahtarlıktan (keyring) çeker. Depo bir yol (path) tutar, veri deposu değeri tutar ve veri deposu erişim günlüğünü saklar. Küçük ekipler için, API desteğine sahip kendi kendine barındırılan bir parola yöneticisi, örneğin bir Vaultwarden sunucusu, aynı işi daha küçük ölçekte görür.

Bir kimlik bilgisi tüm bunların dışında kalır. Kontrol makinenizin sunuculara erişmek için kullandığı SSH anahtarı bir Vault sorunu değildir; çünkü Ansible'ın herhangi bir play'i çalıştırabilmesi için bu anahtara önceden ihtiyacı vardır. Bunu, SSH anahtar yönetimi temelleri doğrultusunda bir agent ve parola (passphrase) ile yönetin.

FAQ

Tüm vars dosyasını mı yoksa sadece gizli dizgiyi mi şifrelemeliyim?

Dosya yalnızca gizli veriler içeriyorsa tamamını şifreleyin; çünkü tek bir komutla hepsini döndürebilir ve düzeni basit tutabilirsiniz. Gizli veriler normal değişkenlerin yanında yer alıyorsa ansible-vault encrypt_string kullanın; bu sayede diff çıktısında yalnızca şifrelenmiş değer değişir ve bir incelemeci hangi değişkenin güncellendiğini görebilir. Buradaki ödünleşim döndürme işlemidir. ansible-vault rekey tüm dosyaları kapsar ancak satır içi !vault bloklarını değiştirmez, bu nedenle bu blokların yeni parola altında elle yeniden oluşturulması gerekir.

Ansible Vault parola dosyası nerede saklanmalıdır?

Deponun dışında, 0600 modunda ve ~/.ansible/vault-prod.txt gibi bir yolda tutulmalıdır. --vault-password-file ile bu dosyayı işaret edin, ansible.cfg içindeki [defaults] altında vault_password_file ayarını yapın veya ortam değişkeni olarak ANSIBLE_VAULT_PASSWORD_FILE değerini atayın. CI süreçlerinde, işin parolayı kendi kimlik bilgisi deposundan geçici bir dosyaya yazmasını sağlayın, değişkeni dışa aktarın ve iş bittiğinde dosyayı silin. Dosya çalıştırılabilir ise Ansible onu çalıştırır ve parolayı standart çıktıdan okur; bu da parolayı diskte saklamak yerine bir anahtarlıktan (keyring) çekmenize olanak tanır.

Staging ve production için farklı vault parolalarını nasıl kullanırım?

Her parolaya --vault-id staging@/path/to/file ve --vault-id prod@/path/to/file ile bir etiket verin ve her ortamın dosyalarını kendi etiketi altında şifreleyin. Çalışma zamanında her iki kimliği de iletin veya [defaults] altındaki vault_identity_list içinde listeleyin. Ansible varsayılan olarak bir dosyanın şifresini çözene kadar elindeki tüm gizli verileri dener; bu nedenle yalnızca dosya başlığıyla eşleşen etikete sahip gizli veriyi denemesini istiyorsanız vault_id_match = True ayarını yapın. Birden fazla kimlik yüklüyken, şifreleme için kullanılacak olanı --encrypt-vault-id ile seçin.

Ansible Vault, parolanın çalışma çıktısında görünmesini engeller mi?

Hayır. Vault, gizli veriyi yalnızca depoda durduğu sürece korur. Bir görev çalıştığında değer düz metin halindedir; ayrıntılı (verbose) bir çalışma veya başarısız bir görev, bu değeri günlüğe taşıyabilir. Kimlik bilgisi işleyen her göreve no_log: true ekleyin, şablon olarak oluşturduğunuz her dosyada kısıtlayıcı mode ve owner izinlerini ayarlayın ve gizli verileri komut argümanı olarak geçmekten kaçının; çünkü bu veriler komut çalışırken hedef sunucudaki süreç listesinde görünür.