[ OK ]Çekirdek başlatılıyor...
~/im/blog
Benimle Çalışın

Konuşalım

Birlikte çalışmakla ilgileniyor ya da bir sorunuz mu var? Yeni projeleri konuşmaya her zaman açığım.

İletişime geçin

Bağlantı Kurun

Beni sosyal medyada ve profesyonel ağlarda bulabilirsiniz.

Gizlilik Politikası (KVKK)Kullanım Koşulları
© 2026 Irfan MiralGeliştirici:irfanMiral.com
AnasayfaHakkımda/ÖzgeçmişBlogİletişim
2026-02-05• 5 dakika okuma

Her Yeni Sunucuda Çalıştırdığım Ansible Playbook'u

DevOps Ansible Otomasyon Linux Yönetimi

Her yeni sunucuda geçtiğim checklist hakkında daha önce yazmıştım: non-root bir kullanıcı, sadece key ile SSH, default-deny bir firewall, Fail2ban, unattended upgrade'ler. Bunu elle yapmak bir saatten az sürüyor ve zor değil. Ama "her sunucu için elle, bir saatten az" ikiden üçten fazla sunucu yönettiğinizde hızla birikiyor, ve küçük tutarsızlıkların sızdığı yer de tam olarak manuel adımlar, bir sunucuya MaxAuthTries 3 giriyor, diğerine girmiyor, çünkü o günü kuran kişinin acelesi vardı.

Çözüm, checklist'i bir playbook'a çevirmek. Her seferinde aynı adımlar, aynı sıra, ve zaten configure edilmiş bir sunucuda tekrar çalıştırıldığında hiçbir şeyi değiştirmeyecek kadar idempotent.

Playbook

---
- name: Baseline hardening for a new server
  hosts: new_servers
  become: true

  vars:
    admin_user: deploy
    ssh_public_key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"

  tasks:
    - name: Create admin user
      user:
        name: "{{ admin_user }}"
        groups: sudo
        shell: /bin/bash
        create_home: true

    - name: Add SSH key for admin user
      authorized_key:
        user: "{{ admin_user }}"
        key: "{{ ssh_public_key }}"

    - name: Disable root login and password auth
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "{{ item.regexp }}"
        line: "{{ item.line }}"
      loop:
        - { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
        - { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
        - { regexp: '^#?MaxAuthTries', line: 'MaxAuthTries 3' }
      notify: restart sshd

    - name: Install UFW and Fail2ban
      apt:
        name: [ufw, fail2ban]
        state: present
        update_cache: true

    - name: Configure UFW defaults
      ufw:
        direction: "{{ item.direction }}"
        policy: "{{ item.policy }}"
      loop:
        - { direction: incoming, policy: deny }
        - { direction: outgoing, policy: allow }

    - name: Allow required ports
      ufw:
        rule: allow
        port: "{{ item }}"
        proto: tcp
      loop: ['22', '80', '443']

    - name: Enable UFW
      ufw:
        state: enabled

    - name: Enable Fail2ban
      systemd:
        name: fail2ban
        enabled: true
        state: started

    - name: Install unattended-upgrades
      apt:
        name: unattended-upgrades
        state: present

  handlers:
    - name: restart sshd
      systemd:
        name: ssh
        state: restarted

Çalıştırmak

Yeni bir sunucu inventory'e ekleniyor ve playbook sadece o host'a karşı çalışıyor:

ansible-playbook -i inventory.ini baseline.yml -l new-server-01

İlk çalıştırma tüm işi yapıyor, kullanıcıyı oluşturuyor, SSH'ı kilitliyor, firewall'u kuruyor. Handler, sshd'i sadece config gerçekten değiştiyse restart ediyor, yani bunun olduğu tek an ilk çalıştırma, sonraki her çalıştırma hiçbir şeyin kaymadığını onaylayan bir no-op.

Asıl mesele neden idempotency

Buradaki gerçek değer ilk gün kazanılan zaman değil, komutları elle yazmak yavaş değil zaten. Mesele, altı ay sonra, belirli bir sunucunun tam işlemden geçip geçmediğinden ya da bir incident sırasında aceleyle kurulup kurulmadığından emin olmadığımda, playbook'u tekrar çalıştırıp öğrenebilmem. Her şey zaten yerindeyse, Ansible sıfır değişiklik raporluyor ve devam ediyorum. Bir şey eksikse, dört beş manuel adımdan hangisinin atlandığını hatırlamaya gerek kalmadan yerinde düzeltiliyor.

Bu playbook bilerek küçük. Uygulama stack'leri kurmuyor ya da projeye özel bir şey configure etmiyor, üzerine başka her şeyin katmanlandığı zemin bu. Uygulama playbook'larından ayrı tutmak onu stabil tutuyor, ve sık değişmeyen bir baseline, her seferinde yeniden okumaya gerek kalmadan güvenebileceğiniz bir şey.

ÖncekiKüçük Projelerin Çoğu İçin Yeten Bir GitLab CI/CD Pipeline'ıSonraki Sunucu Config'leri Neden Kafanda Değil, Git'te Olmalı!