Ansible ile Firewall Kurallarının Merkezi Yönetimi: firewalld ve ufw Modülleri

Onlarca sunucuya tek tek bağlanıp firewall kuralı eklemek, sysadmin hayatının en sinir bozucu anlarından biridir. Bir kural eksik kalır, bir sunucuya farklı yazılmış olur, ya da kim ne zaman ne ekledi bilinmez. Ansible’ın firewall modülleri tam olarak bu kaosa son vermek için tasarlanmış. Bugün firewalld ve ufw modüllerini gerçek dünya senaryolarıyla ele alacağız.

Neden Merkezi Firewall Yönetimi?

Klasik yöntemde şöyle bir tablo ortaya çıkar: 20 sunucun var, her birine SSH açmışsın, bazılarına HTTP/HTTPS eklemişsin, bir kısmına uygulama portları var. Birisi bir sunucuya bağlanıp elle kural eklemiş ama dokümante etmemiş. Altı ay sonra o kurala neden ihtiyaç duyulduğunu kimse bilmiyor.

Ansible ile firewall yönetiminde her kural kod olarak yaşar, Git’te versiyonlanır, kim ne zaman hangi değişikliği yaptı görülür. Yeni bir sunucu kurduğunda aynı playbook’u çalıştırırsın, tüm kurallar otomatik uygulanır. Bu yaklaşımın getirdiği en büyük fayda idempotency: aynı playbook’u kaç kez çalıştırırsan çalıştır, sonuç değişmez.

Temel Kavramlar: firewalld ve ufw Farkı

İki modülü karıştırmadan önce hangisinin nerede kullanıldığını netleştirelim.

firewalld: RHEL, CentOS, Rocky Linux, AlmaLinux ve Fedora tabanlı sistemlerde varsayılan olarak gelir. Zone (bölge) kavramı üzerine kurulmuş, dinamik bir firewall yönetim daemon’ıdır. Servisleri yeniden başlatmadan kural ekleyip çıkarabilirsin.

ufw (Uncomplicated Firewall): Ubuntu ve Debian tabanlı sistemlerde yaygındır. iptables’ın üzerinde daha sade bir arayüz sunar. İsmi “karmaşık olmayan” demek ama yeterince kapsamlıdır.

Kurulum ve Ön Hazırlık

Önce inventory dosyamızı düzenleyelim. Gerçekçi bir senaryo için karma bir ortam düşünelim: bir kısmı RHEL/Rocky, bir kısmı Ubuntu sunucular.

# inventory/hosts.ini
[rhel_servers]
web01 ansible_host=192.168.1.10
web02 ansible_host=192.168.1.11
db01  ansible_host=192.168.1.20

[ubuntu_servers]
app01 ansible_host=192.168.1.30
app02 ansible_host=192.168.1.31
cache01 ansible_host=192.168.1.40

[all:vars]
ansible_user=sysadmin
ansible_become=true
ansible_become_method=sudo

RHEL sunucularında firewalld’nin kurulu ve çalışır olması gerekiyor. Ubuntu’da ufw paketinin kurulu olması gerekiyor. Bunları da Ansible ile halledebiliriz:

# playbooks/firewall_prerequisites.yml
---
- name: Firewall paketlerini hazirla
  hosts: all
  become: true
  tasks:
    - name: RHEL - firewalld kur ve baslat
      ansible.builtin.package:
        name: firewalld
        state: present
      when: ansible_os_family == "RedHat"

    - name: RHEL - firewalld servisini aktif et
      ansible.builtin.service:
        name: firewalld
        state: started
        enabled: true
      when: ansible_os_family == "RedHat"

    - name: Ubuntu - ufw kur
      ansible.builtin.package:
        name: ufw
        state: present
      when: ansible_os_family == "Debian"

firewalld Modülü ile Kural Yönetimi

Temel firewalld Kullanımı

ansible.posix.firewalld modülü, firewalld’nin tüm özelliklerine erişim sağlar. En sık kullandığım parametreler:

state: enabled veya disabled – kuralın aktif olup olmadığı permanent: true veya false – sistem yeniden başlatıldığında da geçerli olsun mu immediate: true veya false – hemen uygulansın mı zone: Hangi zone’a uygulanacak (public, internal, dmz vb.) service: firewalld’nin tanıdığı servis isimleri (http, https, ssh vb.) port: Port numarası ve protokol (8080/tcp gibi)

Dikkat: permanent: true ile immediate: true birlikte kullanmazsanız, kurallar ya sadece şu an geçerli olur ya da sadece yeniden başlatmadan sonra. İkisini birlikte kullanmak en güvenli yaklaşım.

# playbooks/firewalld_web_servers.yml
---
- name: Web sunuculari icin firewall kurallari
  hosts: rhel_servers
  become: true
  tasks:
    - name: SSH portunu ac
      ansible.posix.firewalld:
        service: ssh
        permanent: true
        immediate: true
        state: enabled
        zone: public

    - name: HTTP portunu ac
      ansible.posix.firewalld:
        service: http
        permanent: true
        immediate: true
        state: enabled
        zone: public

    - name: HTTPS portunu ac
      ansible.posix.firewalld:
        service: https
        permanent: true
        immediate: true
        state: enabled
        zone: public

    - name: Ozel uygulama portu ac (8443/tcp)
      ansible.posix.firewalld:
        port: 8443/tcp
        permanent: true
        immediate: true
        state: enabled
        zone: public

Zone Tabanlı Gelişmiş Konfigürasyon

Gerçek dünya senaryosunda genellikle sunucuların birden fazla ağ arayüzü olur. Örneğin bir veritabanı sunucusu hem dış ağa hem iç ağa bağlı olabilir. Bunu zone’larla yönetmek çok daha temiz:

# playbooks/firewalld_db_servers.yml
---
- name: Veritabani sunuculari icin zone yapilandirmasi
  hosts: db01
  become: true
  tasks:
    - name: Internal zone'a network arayzunu ata
      ansible.posix.firewalld:
        zone: internal
        interface: eth1
        permanent: true
        immediate: true
        state: enabled

    - name: MySQL portunu sadece internal zone'da ac
      ansible.posix.firewalld:
        port: 3306/tcp
        zone: internal
        permanent: true
        immediate: true
        state: enabled

    - name: Public zone'da sadece SSH izin ver
      ansible.posix.firewalld:
        service: ssh
        zone: public
        permanent: true
        immediate: true
        state: enabled

    - name: Disaridan MySQL erisimini engelle (public zone'da kapali tut)
      ansible.posix.firewalld:
        port: 3306/tcp
        zone: public
        permanent: true
        immediate: true
        state: disabled

Rich Rule Kullanımı

Bazen basit port açma/kapamadan daha ince ayar gerekir. Belirli bir IP’den gelen trafiği kabul edip diğerlerini reddetmek, ya da rate limiting uygulamak isteyebilirsin. Bunun için rich rule’lar kullanılır:

# playbooks/firewalld_rich_rules.yml
---
- name: Rich rule ornekleri
  hosts: rhel_servers
  become: true
  tasks:
    - name: Sadece yonetim agından SSH izin ver
      ansible.posix.firewalld:
        rich_rule: 'rule family="ipv4" source address="10.0.0.0/8" service name="ssh" accept'
        permanent: true
        immediate: true
        state: enabled
        zone: public

    - name: Belirli IP'den MySQL erisimi
      ansible.posix.firewalld:
        rich_rule: 'rule family="ipv4" source address="192.168.1.30" port port="3306" protocol="tcp" accept'
        permanent: true
        immediate: true
        state: enabled
        zone: internal

    - name: SSH brute force koruması icin rate limiting
      ansible.posix.firewalld:
        rich_rule: 'rule service name="ssh" limit value="10/m" accept'
        permanent: true
        immediate: true
        state: enabled
        zone: public

ufw Modülü ile Kural Yönetimi

Temel ufw Kullanımı

community.general.ufw modülü Ubuntu/Debian sistemler için kullanılır. firewalld’den biraz farklı bir syntax var ama mantık aynı.

Temel parametreler:

state: enabled, disabled, reloaded, reset rule: allow, deny, reject, limit port: Port numarası veya servis adı proto: tcp, udp, any from_ip: Kaynak IP adresi to_ip: Hedef IP adresi direction: in, out, routed delete: true olursa kuralı siler

# playbooks/ufw_app_servers.yml
---
- name: Ubuntu uygulama sunuculari icin ufw kurallari
  hosts: ubuntu_servers
  become: true
  tasks:
    - name: ufw'u aktif et (varsayilan deny ile)
      community.general.ufw:
        state: enabled
        policy: deny
        direction: incoming

    - name: Giden trafiğe varsayilan izin ver
      community.general.ufw:
        policy: allow
        direction: outgoing

    - name: SSH'e izin ver
      community.general.ufw:
        rule: allow
        port: "22"
        proto: tcp

    - name: HTTP portunu ac
      community.general.ufw:
        rule: allow
        port: "80"
        proto: tcp

    - name: HTTPS portunu ac
      community.general.ufw:
        rule: allow
        port: "443"
        proto: tcp

    - name: Node.js uygulama portunu ac
      community.general.ufw:
        rule: allow
        port: "3000"
        proto: tcp
        comment: "Node.js uygulama portu"

IP Bazlı Kısıtlamalar

# playbooks/ufw_ip_restrictions.yml
---
- name: IP bazli erisim kuralları
  hosts: ubuntu_servers
  become: true
  tasks:
    - name: Sadece belirli IP'den Redis erisimi
      community.general.ufw:
        rule: allow
        port: "6379"
        proto: tcp
        from_ip: "192.168.1.10"
        comment: "Web sunucudan Redis erisimi"

    - name: Yonetim agından tam erisim
      community.general.ufw:
        rule: allow
        from_ip: "10.0.0.0/8"
        comment: "Yonetim agi tam erisim"

    - name: Bilinen kotu IP'yi engelle
      community.general.ufw:
        rule: deny
        from_ip: "203.0.113.100"
        comment: "Kotu IP engeli"

    - name: SSH icin rate limiting uygula
      community.general.ufw:
        rule: limit
        port: "22"
        proto: tcp
        comment: "SSH brute force koruması"

Rol Tabanlı Yaklaşım: Gerçek Dünya Senaryosu

Küçük playbook’lar güzel ama büyük ortamlarda rol (role) kullanmak çok daha yönetilebilir. Bir web sunucusu rolü için firewall konfigürasyonunu nasıl organize ederiz?

# roles/webserver/defaults/main.yml
---
firewall_allowed_ports:
  - port: "80"
    proto: tcp
  - port: "443"
    proto: tcp

firewall_allowed_services:  # firewalld icin
  - http
  - https
  - ssh

firewall_management_network: "10.0.0.0/8"
firewall_app_port: "8080"
# roles/webserver/tasks/firewall_rhel.yml
---
- name: RHEL - Temel servisleri ac
  ansible.posix.firewalld:
    service: "{{ item }}"
    permanent: true
    immediate: true
    state: enabled
    zone: public
  loop: "{{ firewall_allowed_services }}"

- name: RHEL - Uygulama portunu ac
  ansible.posix.firewalld:
    port: "{{ firewall_app_port }}/tcp"
    permanent: true
    immediate: true
    state: enabled
    zone: public

- name: RHEL - Yonetim agından tam erisim
  ansible.posix.firewalld:
    rich_rule: 'rule family="ipv4" source address="{{ firewall_management_network }}" accept'
    permanent: true
    immediate: true
    state: enabled
    zone: public
# roles/webserver/tasks/firewall_ubuntu.yml
---
- name: Ubuntu - ufw'u baslat
  community.general.ufw:
    state: enabled
    policy: deny
    direction: incoming

- name: Ubuntu - Temel portları ac
  community.general.ufw:
    rule: allow
    port: "{{ item.port }}"
    proto: "{{ item.proto }}"
  loop: "{{ firewall_allowed_ports }}"

- name: Ubuntu - Uygulama portunu ac
  community.general.ufw:
    rule: allow
    port: "{{ firewall_app_port }}"
    proto: tcp

- name: Ubuntu - Yonetim agına tam erisim
  community.general.ufw:
    rule: allow
    from_ip: "{{ firewall_management_network }}"
# roles/webserver/tasks/main.yml
---
- name: RHEL firewall yapilandirması
  ansible.builtin.include_tasks: firewall_rhel.yml
  when: ansible_os_family == "RedHat"

- name: Ubuntu firewall yapilandirması
  ansible.builtin.include_tasks: firewall_ubuntu.yml
  when: ansible_os_family == "Debian"

Bu yapı sayesinde tek bir rol hem RHEL hem Ubuntu’da çalışıyor. ansible_os_family fact’i sayesinde doğru görev dosyası seçiliyor.

Mevcut Kuralları Kontrol Etme ve Doğrulama

Playbook çalıştırdıktan sonra kuralların gerçekten uygulandığını doğrulamak için check mode ve doğrulama task’ları eklemek iyi bir pratik:

# playbooks/verify_firewall.yml
---
- name: Firewall kurallarini dogrula
  hosts: all
  become: true
  tasks:
    - name: RHEL - Aktif kurallari listele
      ansible.builtin.command: firewall-cmd --list-all
      register: firewalld_rules
      changed_when: false
      when: ansible_os_family == "RedHat"

    - name: RHEL - Kural ciktisini goster
      ansible.builtin.debug:
        var: firewalld_rules.stdout_lines
      when: ansible_os_family == "RedHat"

    - name: Ubuntu - Aktif kurallari listele
      ansible.builtin.command: ufw status verbose
      register: ufw_rules
      changed_when: false
      when: ansible_os_family == "Debian"

    - name: Ubuntu - Kural ciktisini goster
      ansible.builtin.debug:
        var: ufw_rules.stdout_lines
      when: ansible_os_family == "Debian"

Playbook’u her zaman önce --check modunda çalıştırmayı alışkanlık haline getir:

ansible-playbook -i inventory/hosts.ini playbooks/firewall_web_servers.yml --check --diff

--diff flag’i neyin değişeceğini gösterir. Prod ortamına uygulamadan önce bu çıktıyı incelemek ciddi kazaları önler.

Sık Yapılan Hatalar ve Çözümleri

SSH’i kapatmak: En klasik hata. Eğer default policy’yi deny yapıp SSH kuralını eklemeyi unutursan sunucuya erişimi kaybedersin. Bunun için SSH kuralını her zaman listenin başına koy ve ayrı bir task olarak yaz.

permanent ve immediate karışıklığı: firewalld’de permanent: true tek başına yetmez, kuralı hemen uygulamaz. immediate: true eklemezsen sunucuyu yeniden başlatana kadar kural aktif olmaz. İkisini birlikte kullanmayı kural haline getir.

ufw reset sorunu: ufw reset komutu tüm kuralları siler. Playbook’ta state: reset kullanırken dikkatli ol, ardından tüm kuralları yeniden tanımlamalısın.

Zone kavramını atlamak: firewalld’de zone belirtmezsen varsayılan zone kullanılır. Bu bazen istediğin olmayabilir. Her task’ta zone parametresini açıkça belirt.

Ekip Ortamında İş Akışı

Bu playbook’ları bir ekiple yönetirken şu yapıyı öneririm:

  • Her ortam için ayrı inventory dosyası (dev, staging, prod)
  • Firewall kuralları için ayrı bir Git branch’i, merge request zorunluluğu
  • Prod’a uygulamadan önce staging’de test
  • Playbook çıktılarını bir log sunucusuna veya Slack’e gönderen notification task’ları
  • Her değişikliği bir ticket numarasıyla etiketle (tag veya yorum olarak)

Bu yaklaşımla “kim bu portu açtı” sorusu Git log’una bakılarak saniyeler içinde cevaplanır.

Sonuç

Ansible’ın firewalld ve ufw modülleri, firewall yönetimini elle uğraşmaktan çıkarıp kod tabanına taşımanın en temiz yolu. Başlangıçta playbook yazmak biraz zaman alıyor ama karşılığını fazlasıyla veriyor: tekrarlanabilir konfigürasyonlar, izlenebilir değişiklikler ve insani hatalara karşı koruma.

Özellikle mixed ortamlar (hem RHEL hem Ubuntu) için ansible_os_family fact’i ile koşullu görevler yazmak, tek bir otomasyon katmanından her iki sistemi yönetmeni sağlıyor. Rol bazlı yaklaşımla da bu kuralları farklı sunucu tipleri arasında kolayca paylaşabilirsin.

En önemli öneri: SSH kuralını asla unutma, her zaman --check ile başla ve değişiklikleri küçük tutup adım adım uygula. Firewall bir kez yanlış yapılandırıldığında sunucuya erişimi geri kazanmak saatler alabilir.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir