Ansible ile Firewall Kural Yönetimi: iptables ve firewalld Otomasyonu
Firewall kurallarını elle yönetmek, onlarca sunucu olduğunda kabusa dönüşür. Bir kural eklemeyi unutursun, test ortamıyla production arasında farklılıklar oluşur, değişiklikleri kim yaptı bilemezsin. Ansible bu karmaşayı ortadan kaldırıyor. Hem iptables hem de firewalld için güçlü modüller sunuyor ve tüm kural yönetimini kod olarak ifade etmeni sağlıyor. Bu yazıda gerçek dünya senaryolarıyla Ansible’ın firewall otomasyon yeteneklerini derinlemesine inceleyeceğiz.
Neden Ansible ile Firewall Yönetimi?
Manuel firewall yönetiminin sorunları saymakla bitmez. Sunucu sayısı arttıkça tutarsızlıklar kaçınılmaz hale gelir. Bir sysadmin SSH erişimini açık bırakır, diğeri unutur. Audit loglarına bakarsın, kim ne zaman hangi kuralı ekledi belli değildir.
Ansible bu sürecin önüne geçiyor:
- İdempotent yapı: Playbook’u kaç kez çalıştırsan da sonuç aynı olur, kural zaten varsa tekrar eklenmez
- Version control entegrasyonu: Tüm firewall kuralların Git’te, değişiklik geçmişi şeffaf
- Merkezi yönetim: Yüzlerce sunucuya aynı anda kural uygulayabilirsin
- Test edilebilirlik: Staging ortamında önce test et, sonra production’a taşı
- Hata azaltma: Yazım hataları ve unutulan adımlar tarihe karışır
iptables Yönetimi
Temel iptables Modülü
Ansible’ın ansible.builtin.iptables modülü, doğrudan iptables kurallarını yönetmeni sağlar. CentOS 7 öncesi sistemlerde ve iptables’ı tercih eden ortamlarda sıklıkla kullanılır.
Basit bir HTTP/HTTPS erişim kuralı ekleyelim:
---
- name: Temel Web Sunucusu Firewall Kuralları
hosts: webservers
become: yes
tasks:
- name: HTTP trafiğine izin ver
ansible.builtin.iptables:
chain: INPUT
protocol: tcp
destination_port: 80
jump: ACCEPT
comment: "Ansible tarafından yönetilir - HTTP"
- name: HTTPS trafiğine izin ver
ansible.builtin.iptables:
chain: INPUT
protocol: tcp
destination_port: 443
jump: ACCEPT
comment: "Ansible tarafından yönetilir - HTTPS"
- name: SSH erişimine izin ver
ansible.builtin.iptables:
chain: INPUT
protocol: tcp
destination_port: 22
jump: ACCEPT
comment: "Ansible tarafından yönetilir - SSH"
- name: Loopback trafiğine izin ver
ansible.builtin.iptables:
chain: INPUT
in_interface: lo
jump: ACCEPT
- name: Kurulu bağlantılara izin ver
ansible.builtin.iptables:
chain: INPUT
ctstate: ESTABLISHED,RELATED
jump: ACCEPT
IP Bazlı Kısıtlamalar
Gerçek dünyada genellikle belirli IP adreslerinden belirli portlara erişimi kısıtlaman gerekir. Örneğin, veritabanı sunucusuna sadece uygulama sunucularından erişim:
---
- name: Veritabanı Sunucusu Güvenlik Kuralları
hosts: database_servers
become: yes
vars:
app_servers:
- "10.0.1.10"
- "10.0.1.11"
- "10.0.1.12"
admin_network: "10.0.100.0/24"
tasks:
- name: Uygulama sunucularından MySQL erişimine izin ver
ansible.builtin.iptables:
chain: INPUT
protocol: tcp
source: "{{ item }}"
destination_port: 3306
jump: ACCEPT
comment: "App server DB erişimi"
loop: "{{ app_servers }}"
- name: Admin ağından SSH erişimine izin ver
ansible.builtin.iptables:
chain: INPUT
protocol: tcp
source: "{{ admin_network }}"
destination_port: 22
jump: ACCEPT
comment: "Admin SSH erişimi"
- name: Diğer tüm gelen trafiği engelle
ansible.builtin.iptables:
chain: INPUT
policy: DROP
iptables Kurallarını Kalıcı Hale Getirmek
iptables kurallarının en büyük sorunu, sistem yeniden başladığında kaybolmasıdır. Bunu çözmek için iptables-save kullanmalısın:
---
- name: iptables Kurallarını Kalıcı Hale Getir
hosts: all
become: yes
tasks:
- name: iptables-persistent paketini kur (Debian/Ubuntu)
ansible.builtin.apt:
name: iptables-persistent
state: present
when: ansible_os_family == "Debian"
- name: iptables-services paketini kur (RHEL/CentOS)
ansible.builtin.yum:
name: iptables-services
state: present
when: ansible_os_family == "RedHat"
- name: Kuralları kaydet (Debian/Ubuntu)
ansible.builtin.shell: |
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
when: ansible_os_family == "Debian"
changed_when: true
- name: iptables servisini etkinleştir (RHEL/CentOS)
ansible.builtin.service:
name: iptables
enabled: yes
state: started
when: ansible_os_family == "RedHat"
- name: Kuralları kaydet (RHEL/CentOS)
ansible.builtin.shell: service iptables save
when: ansible_os_family == "RedHat"
changed_when: true
firewalld Yönetimi
firewalld Temelleri
firewalld, RHEL/CentOS 7 ve sonrası ile modern Linux dağıtımlarında varsayılan firewall çözümüdür. Zone kavramıyla çalışır ve dinamik kural değişikliğini destekler. Ansible’ın ansible.posix.firewalld modülü bu sistemi son derece temiz bir şekilde yönetmeni sağlar.
---
- name: firewalld Temel Yapılandırması
hosts: rhel_servers
become: yes
tasks:
- name: firewalld'ı kur
ansible.builtin.yum:
name: firewalld
state: present
- name: firewalld'ı başlat ve etkinleştir
ansible.builtin.service:
name: firewalld
state: started
enabled: yes
- name: HTTP servisine izin ver
ansible.posix.firewalld:
service: http
permanent: yes
state: enabled
zone: public
- name: HTTPS servisine izin ver
ansible.posix.firewalld:
service: https
permanent: yes
state: enabled
zone: public
- name: SSH servisine izin ver
ansible.posix.firewalld:
service: ssh
permanent: yes
state: enabled
zone: public
- name: Kuralları yeniden yükle
ansible.builtin.command: firewall-cmd --reload
changed_when: true
Zone Bazlı Yapılandırma
firewalld’ın en güçlü özelliği zone yönetimidir. Farklı ağ segmentleri için farklı güvenlik politikaları tanımlayabilirsin:
---
- name: firewalld Zone Yapılandırması
hosts: edge_servers
become: yes
vars:
internal_zone: "internal"
dmz_zone: "dmz"
internal_interface: "eth1"
dmz_interface: "eth2"
tasks:
- name: Internal zone'a interface ata
ansible.posix.firewalld:
zone: "{{ internal_zone }}"
interface: "{{ internal_interface }}"
permanent: yes
state: enabled
- name: DMZ zone'a interface ata
ansible.posix.firewalld:
zone: "{{ dmz_zone }}"
interface: "{{ dmz_interface }}"
permanent: yes
state: enabled
- name: Internal zone'da yönetim portlarına izin ver
ansible.posix.firewalld:
zone: "{{ internal_zone }}"
port: "{{ item }}/tcp"
permanent: yes
state: enabled
loop:
- "22"
- "9090"
- "8080"
- name: DMZ zone'da sadece web portlarına izin ver
ansible.posix.firewalld:
zone: "{{ dmz_zone }}"
port: "{{ item }}/tcp"
permanent: yes
state: enabled
loop:
- "80"
- "443"
- name: DMZ'den internal'a geçişi engelle
ansible.posix.firewalld:
zone: "{{ dmz_zone }}"
rich_rule: "rule family='ipv4' source address='172.16.0.0/24' reject"
permanent: yes
state: enabled
- name: Yapılandırmayı yeniden yükle
ansible.builtin.command: firewall-cmd --reload
changed_when: true
Rich Rules Kullanımı
firewalld’ın rich rules özelliği, iptables’a benzer detaylı kural tanımlamanı sağlar:
---
- name: firewalld Rich Rules Yapılandırması
hosts: application_servers
become: yes
vars:
monitoring_server: "10.0.200.50"
backup_server: "10.0.200.60"
allowed_admin_ips:
- "192.168.1.100"
- "192.168.1.101"
tasks:
- name: Monitoring sunucusundan SNMP erişimine izin ver
ansible.posix.firewalld:
rich_rule: >
rule family='ipv4'
source address='{{ monitoring_server }}/32'
port port='161' protocol='udp'
accept
permanent: yes
state: enabled
zone: public
- name: Backup sunucusundan rsync erişimine izin ver
ansible.posix.firewalld:
rich_rule: >
rule family='ipv4'
source address='{{ backup_server }}/32'
port port='873' protocol='tcp'
accept
permanent: yes
state: enabled
zone: public
- name: Admin IP'lerinden SSH'a izin ver
ansible.posix.firewalld:
rich_rule: >
rule family='ipv4'
source address='{{ item }}/32'
service name='ssh'
accept
permanent: yes
state: enabled
zone: public
loop: "{{ allowed_admin_ips }}"
- name: Brute force koruması için rate limiting uygula
ansible.posix.firewalld:
rich_rule: >
rule family='ipv4'
service name='ssh'
limit value='3/m'
accept
permanent: yes
state: enabled
zone: public
Gerçek Dünya Senaryosu: Çok Katmanlı Uygulama Güvenliği
Diyelim ki üç katmanlı bir uygulaman var: web sunucuları, uygulama sunucuları ve veritabanı sunucuları. Her katmanın farklı güvenlik kurallarına ihtiyacı var.
Inventory Yapısı
# inventory/production/hosts.ini
[webservers]
web01 ansible_host=10.0.1.10
web02 ansible_host=10.0.1.11
[appservers]
app01 ansible_host=10.0.2.10
app02 ansible_host=10.0.2.11
[dbservers]
db01 ansible_host=10.0.3.10
[production:children]
webservers
appservers
dbservers
Grup Değişkenleri ile Esnek Kural Yönetimi
# group_vars/webservers.yml
firewall_type: firewalld
allowed_services:
- http
- https
allowed_ports:
- port: "8080"
proto: tcp
blocked_ips:
- "185.220.101.0/24" # Bilinen kötü amaçlı blok
- "45.155.205.0/24"
# group_vars/appservers.yml
firewall_type: firewalld
allowed_services:
- ssh
allowed_ports:
- port: "8443"
proto: tcp
- port: "9000"
proto: tcp
allowed_sources:
- "10.0.1.0/24" # Sadece web sunucu ağından erişim
# group_vars/dbservers.yml
firewall_type: iptables
db_port: 3306
allowed_db_sources:
- "10.0.2.10"
- "10.0.2.11"
Ana Playbook
---
- name: Çok Katmanlı Uygulama Firewall Yönetimi
hosts: production
become: yes
pre_tasks:
- name: Mevcut firewall durumunu kontrol et
ansible.builtin.command: systemctl is-active firewalld
register: firewalld_status
changed_when: false
failed_when: false
roles:
- role: firewall_common
- role: firewall_webserver
when: inventory_hostname in groups['webservers']
- role: firewall_appserver
when: inventory_hostname in groups['appservers']
- role: firewall_dbserver
when: inventory_hostname in groups['dbservers']
post_tasks:
- name: Firewall durumunu doğrula
ansible.builtin.command: firewall-cmd --list-all
register: firewall_status
changed_when: false
when: ansible_os_family == "RedHat"
- name: Firewall durumunu göster
ansible.builtin.debug:
var: firewall_status.stdout_lines
when: ansible_os_family == "RedHat"
Firewall Kurallarını Test Etmek
Kural uyguladıktan sonra doğrulama yapmak kritik önem taşır. Ansible ile bu süreci de otomatize edebilirsin:
---
- name: Firewall Kurallarını Doğrula
hosts: all
become: yes
tasks:
- name: Açık portları listele (ss komutu ile)
ansible.builtin.command: ss -tlnp
register: open_ports
changed_when: false
- name: firewalld aktif kurallarını kontrol et
ansible.builtin.command: firewall-cmd --list-all-zones
register: fw_zones
changed_when: false
when: ansible_os_family == "RedHat"
- name: iptables kurallarını kontrol et
ansible.builtin.command: iptables -L -n -v
register: ipt_rules
changed_when: false
when: ansible_os_family == "Debian"
- name: Kritik portların açık olduğunu doğrula
ansible.builtin.wait_for:
host: "{{ ansible_default_ipv4.address }}"
port: "{{ item }}"
timeout: 5
state: started
loop:
- 22
- 80
- 443
ignore_errors: yes
register: port_check
- name: Port kontrol sonuçlarını göster
ansible.builtin.debug:
msg: "Port {{ item.item }}: {{ 'AÇIK' if not item.failed else 'KAPALI' }}"
loop: "{{ port_check.results }}"
Handler Kullanımı ile Akıllı Yeniden Yükleme
Firewall kuralları değiştiğinde servisi yeniden yüklemek gerekir. Bu işlemi handler’larla şöyle düzenleyebilirsin:
---
- name: Handler Tabanlı Firewall Yönetimi
hosts: all
become: yes
handlers:
- name: Reload firewalld
ansible.builtin.command: firewall-cmd --reload
changed_when: true
- name: Save iptables rules
ansible.builtin.shell: |
if command -v iptables-save > /dev/null 2>&1; then
iptables-save > /etc/iptables/rules.v4
fi
changed_when: true
tasks:
- name: Web servisi kuralı ekle
ansible.posix.firewalld:
service: http
permanent: yes
state: enabled
zone: public
notify: Reload firewalld
- name: Özel port kuralı ekle
ansible.posix.firewalld:
port: "8080/tcp"
permanent: yes
state: enabled
zone: public
notify: Reload firewalld
- name: iptables kuralı ekle
ansible.builtin.iptables:
chain: INPUT
protocol: tcp
destination_port: 9000
jump: ACCEPT
notify: Save iptables rules
when: ansible_os_family == "Debian"
İpuçları ve Dikkat Edilmesi Gerekenler
SSH bağlantısını kesmemek: Firewall kurallarını uygularken ilk olarak SSH kuralını ekle, en sona da varsayılan politikayı sıkılaştır. Aksi halde Ansible’ın sunucuya erişimi kesilir.
Kalıcılık kontrolü: firewalld’da permanent: yes ile birlikte immediate etkisi için ayrıca --reload yapmayı unutma. Sadece permanent: yes koymak kuralı dosyaya yazar ama hemen aktif etmez.
Idempotency testi: Playbook’u iki kez çalıştır, ikinci çalışmada hiçbir görev “changed” durumuna geçmemeli.
Rol ayırımı: Web sunucusu, uygulama sunucusu ve veritabanı sunucusu kurallarını ayrı role’lere yaz. Hem okunabilirliği artırır hem de bakımı kolaylaştırır.
Değişken hassasiyeti: İzin verilen IP listelerini group_vars veya Ansible Vault’ta tut. Kod içine gömmekten kaçın.
Önce test ortamı: Her yeni firewall politikasını mutlaka staging’de dene. Production’da bir kural hatası ciddi kesintiye yol açabilir.
Mevcut kuralları temizlemek: Bazen sıfırdan başlamak gerekir. flush parametresiyle tüm zincirleri temizleyebilirsin ama bu işlemi yaparken çok dikkatli ol.
Log kuralları ekle: Engellenen trafiği loglayan kurallar eklemek, sorun giderme sürecini büyük ölçüde kolaylaştırır. firewalld’da rich rules içinde log aksiyonunu kullanabilirsin.
Sonuç
Ansible ile firewall yönetimi, güvenlik politikalarını kod olarak ifade etmenin en temiz yollarından biri. iptables’ın esnekliği ile firewalld’ın zone bazlı yaklaşımı arasındaki farkı anlayıp her iki sistemi de Ansible ile yönetebilmek, sysadmin olarak güvenlik otomasyonunda ciddi bir olgunluk seviyesi demek.
Burada anlattığımız yapıyı kurduğunda, yeni bir sunucu eklediğinde tek yapman gereken inventory’ye ekleyip playbook’u çalıştırmak. Tüm güvenlik kuralları otomatik olarak uygulanıyor, version history Git’te, değişiklikler denetlenebilir. Onlarca sunucuya aynı anda kural uygulamak, bireysel sunucularda hata riskini ortadan kaldırmak ve firewall politikalarını yazılım geliştirme sürecine dahil etmek artık çok daha kolay.
Bir sonraki adımda bu playbook’ları CI/CD pipeline’ına entegre edip her değişiklikte otomatik test çalıştırmayı hedefleyebilirsin. Güvenlik konfigürasyonlarının da kod gibi test edilmesi gerektiğini hiç unutma.
