Her Yeni Sunucuda Çalıştırdığım Ansible Playbook'u
Reklam
Daha önce her yeni sunucuda mutlaka uyguladığım kontrol listesi hakkında yazmıştım: root olmayan bir kullanıcı, sadece anahtar tabanlı (key-only) SSH, varsayılanı "reddet" olan bir güvenlik duvarı, Fail2ban ve otomatik güncellemeler. Bunları manuel olarak yapmak bir saatten az sürer. Zor bir iş değildir.
Ancak "her sunucu için manuel olarak bir saatten az", eğer üçten fazla sunucu yönetiyorsanız çok hızlı bir şekilde devasa bir yüke dönüşür. Dahası, küçük tutarsızlıkların sisteme sızdığı yer tam olarak manuel adımlardır. O gün sunucuyu kuran kişinin acelesi olduğu için bir sunucuda MaxAuthTries 3 ayarı yapılırken diğerinde unutulur.
Bunun kesin çözümü, kontrol listesini bir playbook'a çevirmektir. Her seferinde aynı adımlar, aynı sırada işlenir. Ve en önemlisi idempotent olmasıdır: zaten ayarlanmış bir sunucuda tekrar çalıştırdığınızda hiçbir şeyi bozmaz veya değiştirmez.
Playbook'un kendisi
---
- name: Yeni bir sunucu için temel güvenlik (hardening)
hosts: new_servers
become: true
vars:
admin_user: deploy
ssh_public_key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
tasks:
- name: Admin kullanıcısı oluştur
user:
name: "{{ admin_user }}"
groups: sudo
shell: /bin/bash
create_home: true
- name: Admin kullanıcısı için SSH anahtarı ekle
authorized_key:
user: "{{ admin_user }}"
key: "{{ ssh_public_key }}"
- name: SSH yapılandırmasını sıkılaştır (cloud-init varsayılanlarını ezer)
copy:
dest: /etc/ssh/sshd_config.d/99-hardening.conf
content: |
PermitRootLogin no
PasswordAuthentication no
MaxAuthTries 3
mode: '0644'
notify: restart sshd
- name: UFW ve Fail2ban kur
apt:
name: [ufw, fail2ban]
state: present
update_cache: true
- name: UFW varsayılanlarını ayarla
ufw:
direction: "{{ item.direction }}"
policy: "{{ item.policy }}"
loop:
- { direction: incoming, policy: deny }
- { direction: outgoing, policy: allow }
- name: Gerekli portlara izin ver
ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop: ['22', '80', '443']
- name: UFW'yi aktifleştir
ufw:
state: enabled
- name: Fail2ban'i aktifleştir
systemd:
name: fail2ban
enabled: true
state: started
- name: Unattended-upgrades paketini kur
apt:
name: unattended-upgrades
state: present
handlers:
- name: restart sshd
systemd:
name: ssh
state: restarted
Çalıştırma
Yeni bir sunucu ayağa kalktığında envantere (inventory) eklenir ve playbook sadece o hedefe karşı çalıştırılır:
ansible-playbook -i inventory.ini baseline.yml -l yeni-sunucu-01 -u root
Sıfır bir sunucuda elimizdeki tek kullanıcı root olduğu için ilk çalıştırma mutlaka root olarak yapılmalıdır. Playbook tamamlandığında root girişi zaten kapatılmış olacaktır. Bundan sonraki tüm işlemler için giriş noktanız deploy kullanıcısıdır:
ansible-playbook -i inventory.ini baseline.yml -l yeni-sunucu-01 -u deploy
İlk çalıştırma tüm ağır yükü kaldırır: kullanıcıyı oluşturur, SSH'ı kilitler ve güvenlik duvarını kurar. Sondaki handler, sshd servisini yalnızca yapılandırma dosyasında gerçekten bir değişiklik yapıldıysa yeniden başlatır. Bundan sonraki her çalıştırma, sistemde hiçbir ayarın kaymadığını (drift) onaylayan boş bir işlemden ibaret olacaktır.
İnteraktif olarak deneyin: Bu sitedeki Ansible Baseline Generator aracı, bu playbook'u bir kullanıcı arayüzü üzerinden yapılandırmanıza olanak tanır. Admin kullanıcı adını, SSH portunu ayarlayabilir, dahil edilecek modülleri seçebilir ve hazırlanan tam YAML çıktısını kopyalayabilirsiniz.
Asıl mesele neden "idempotency" (tekrar edilebilirlik)?
Buradaki asıl değer, ilk gün tasarruf edilen o kısacık zaman değildir. Komutları elle yazmak o kadar da yavaşlatmaz. Asıl değer şudur: altı ay sonra, bir sunucunun gerçekten düzgün şekilde ayarlandığından mı yoksa bir kriz anında aceleye mi getirildiğinden emin olamadığımda, bu playbook'u tekrar çalıştırıp gerçeği görebilirim.
Eğer her şey olması gerektiği gibiyse, Ansible sıfır değişiklik raporlar ve ben yoluma devam ederim. Eğer eksik bir şey varsa, o an düzeltilir. O beş manuel adımdan hangisinin atlandığını hatırlamak zorunda kalmam.
Bu playbook kasıtlı olarak kısa tutulmuştur. Uygulama yığınlarını (stack) veya projeye özel servisleri kurmaz. Üzerine başka herhangi bir şey inşa edilmeden önce her sunucunun oturması gereken en temel zemindir. Bunu projeye özel playbook'lardan ayrı tutmak stabil kalmasını sağlar. Sık değişmeyen bir temel, her seferinde kodu baştan sona okumadan körü körüne güvenebileceğiniz bir temeldir.
Reklam