Exim ile sanal domain ve kullanıcı yönetimi, bir mail sunucusunu gerçek anlamda üretken hale getirmenin en kritik adımlarından biridir. Tek bir sunucu üzerinde onlarca, hatta yüzlerce farklı domain için posta hizmeti vermek istiyorsanız, Exim’in esnek yapılandırma sistemi tam da bunun için tasarlanmış. Bu yazıda sıfırdan sanal domain kurulumundan kullanıcı yönetimine, alias tanımlamalarından MySQL destekli yapılandırmalara kadar her şeyi ele alacağız.
Sanal Domain Yönetimine Giriş
Exim, varsayılan kurulumda yalnızca yerel sistem kullanıcıları ve tek bir domain için çalışır. Oysa gerçek dünya senaryolarında durum çok daha karmaşık: Bir müşteriniz için firma-a.com, başka bir müşteri için firma-b.net üzerinde mail hizmeti vermek zorundasınız. Bunların her birinin kendi kullanıcıları, kendi alias’ları ve kendi yönlendirme kuralları olacak.
Exim’de sanal domain yönetiminin temeli üç kavrama dayanır:
- Virtual domains: Exim’in sahip çıkacağı domain listesi
- Virtual users: Bu domainlere ait posta kutuları
- Routers ve Transports: Postanın nereye ve nasıl teslim edileceğini belirleyen yapılar
Yapılandırma dosyaları genellikle /etc/exim4/ altında bulunur. Debian/Ubuntu sistemlerde bölünmüş yapılandırma (split configuration) kullanılırken, RHEL/CentOS sistemlerde tek dosya (/etc/exim/exim.conf) tercih edilir. Bu yazıda her iki yaklaşımı da ele alacağız.
Temel Yapılandırma Dosyasını Hazırlamak
Öncelikle mevcut Exim yapılandırmasını yedekleyelim ve çalışma ortamımızı hazırlayalım:
# Mevcut yapılandırmayı yedekle
cp /etc/exim4/exim4.conf.template /etc/exim4/exim4.conf.template.bak
# Yapılandırma dosyasını düzenle
nano /etc/exim4/exim4.conf.template
Debian tabanlı sistemlerde update-exim4.conf komutu bölünmüş yapılandırma dosyalarını birleştirerek çalıştırılabilir bir konfig üretir. Ana ayar dosyası şudur:
# /etc/exim4/update-exim4.conf.conf içeriğini kontrol et
cat /etc/exim4/update-exim4.conf.conf
Temel parametreleri şu şekilde ayarlıyoruz:
dc_eximconfig_configtype='internet'
dc_other_hostnames='mailserver.example.com'
dc_local_interfaces='0.0.0.0.25 : 0.0.0.0.587 : 0.0.0.0.465'
dc_readhost=''
dc_relay_domains=''
dc_minimaldns='false'
dc_relay_nets=''
dc_smarthost=''
CFILEMODE='644'
dc_use_split_config='true'
dc_hide_mailname='false'
dc_mailname_in_oh='true'
dc_localdelivery='maildir_home'
Sanal Domain Listesini Oluşturmak
Exim’in hangi domainlere sahip çıkacağını bir dosya üzerinden yönetmek en pratik yaklaşımdır. Bu sayede yapılandırma dosyasını her domain eklediğinizde yeniden başlatmak zorunda kalmazsınız.
# Sanal domain listesi oluştur
cat > /etc/exim4/virtual/domains << 'EOF'
firma-a.com
firma-b.net
startup-xyz.org
EOF
# Dosya izinlerini ayarla
chmod 644 /etc/exim4/virtual/domains
chown root:Debian-exim /etc/exim4/virtual/domains
Exim yapılandırmasında bu dosyayı bir liste olarak tanımlıyoruz:
# exim4.conf.template veya conf.d/main/00_local_macros içine ekle
domainlist virtual_domains = lsearch;/etc/exim4/virtual/domains
Bu tanımdan sonra virtual_domains listesi, tüm router ve transport tanımlarında kullanılabilir hale gelir.
Sanal Kullanıcı Dosyalarını Yapılandırmak
Her domain için ayrı bir kullanıcı dosyası tutmak, yönetimi kolaylaştırır. Dosya formatı kullanici:hedef_dizin şeklindedir:
# firma-a.com için kullanıcı dosyası
mkdir -p /etc/exim4/virtual/
cat > /etc/exim4/virtual/firma-a.com << 'EOF'
info: /var/mail/virtual/firma-a.com/info/
destek: /var/mail/virtual/firma-a.com/destek/
ceo: /var/mail/virtual/firma-a.com/ceo/
genel: info
EOF
# firma-b.net için kullanıcı dosyası
cat > /etc/exim4/virtual/firma-b.net << 'EOF'
admin: /var/mail/virtual/firma-b.net/admin/
satis: /var/mail/virtual/firma-b.net/satis/
iletisim: admin
EOF
Burada genel: info satırı bir alias tanımı: [email protected]‘a gelen postalar [email protected] posta kutusuna yönlendirilecek. iletisim: admin de benzer şekilde çalışır.
Posta kutusu dizinlerini oluşturalım:
# Sanal kullanıcılar için sistem kullanıcısı oluştur
groupadd -g 5000 vmail
useradd -u 5000 -g vmail -d /var/mail/virtual -s /usr/sbin/nologin vmail
# Dizin yapısını oluştur
mkdir -p /var/mail/virtual/firma-a.com/{info,destek,ceo}
mkdir -p /var/mail/virtual/firma-b.net/{admin,satis}
# Maildir formatında alt dizinler oluştur
for dir in /var/mail/virtual/firma-a.com/info
/var/mail/virtual/firma-a.com/destek
/var/mail/virtual/firma-a.com/ceo; do
mkdir -p "$dir"/{cur,new,tmp}
done
# İzinleri düzenle
chown -R vmail:vmail /var/mail/virtual
chmod -R 700 /var/mail/virtual
Router Tanımlamak
Exim’de postanın akışını belirleyen en önemli bileşen router‘lardır. Sanal domain kullanıcıları için bir router tanımlamamız gerekiyor:
# /etc/exim4/conf.d/router/350_virtual_domains
virtual_user_router:
driver = redirect
domains = +virtual_domains
data = ${lookup{$local_part}lsearch{/etc/exim4/virtual/$domain}}
file_transport = address_file
pipe_transport = address_pipe
no_more
Bu router şunu yapar: Eğer gelen posta virtual_domains listesindeki bir domain’e aitse, ilgili domain dosyasında local_part (kullanıcı adı) araması yapar. Bulursa belirtilen hedefe yönlendirir, bulamazsa postayı reddeder.
Maildir teslimatı için bir transport da tanımlamamız gerekiyor:
# /etc/exim4/conf.d/transport/35_virtual_maildir
virtual_maildir_transport:
driver = appendfile
maildir_format = true
create_directory = true
directory = ${extract{1}{:}{${lookup{$local_part}lsearch{/etc/exim4/virtual/$domain}}}}
delivery_date_add
envelope_to_add
return_path_add
group = vmail
user = vmail
mode = 0600
Wildcard ve Catchall Kullanımı
Gerçek dünyada müşteriler sık sık “domain’e gelen tüm postalar bir kutuya düşsün” der. Buna catchall diyoruz. Exim’de bunu şu şekilde ayarlıyoruz:
# firma-b.net için catchall ekle
echo "*: /var/mail/virtual/firma-b.net/admin/" >> /etc/exim4/virtual/firma-b.net
Ya da router seviyesinde wildcard desteği ekleyebiliriz:
virtual_catchall_router:
driver = redirect
domains = +virtual_domains
condition = ${if exists{/etc/exim4/virtual/$domain}{yes}{no}}
data = ${lookup{*}lsearch{/etc/exim4/virtual/$domain}}
file_transport = virtual_maildir_transport
no_more
Dikkat etmeniz gereken önemli bir nokta: Catchall’u etkinleştirirseniz spam yükünüz ciddi ölçüde artabilir. Bu nedenle catchall router’ınızı mutlaka spam filtreleme ile birlikte kullanın.
MySQL ile Sanal Kullanıcı Yönetimi
Onlarca domain ve yüzlerce kullanıcı yönetiyorsanız, düz metin dosyaları yetersiz kalır. MySQL entegrasyonu burada devreye girer. Önce veritabanı şemasını oluşturalım:
CREATE DATABASE mailserver CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE mailserver;
CREATE TABLE virtual_domains (
id INT AUTO_INCREMENT PRIMARY KEY,
domain VARCHAR(255) NOT NULL UNIQUE,
active TINYINT(1) DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE virtual_users (
id INT AUTO_INCREMENT PRIMARY KEY,
domain_id INT NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
maildir VARCHAR(255) NOT NULL,
quota BIGINT DEFAULT 104857600,
active TINYINT(1) DEFAULT 1,
FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
);
CREATE TABLE virtual_aliases (
id INT AUTO_INCREMENT PRIMARY KEY,
domain_id INT NOT NULL,
source VARCHAR(255) NOT NULL,
destination VARCHAR(255) NOT NULL,
active TINYINT(1) DEFAULT 1,
FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
);
-- Örnek veri ekle
INSERT INTO virtual_domains (domain) VALUES ('firma-a.com'), ('firma-b.net');
INSERT INTO virtual_users (domain_id, email, password, maildir)
VALUES (1, '[email protected]', '{SHA256}hashedpassword', '/var/mail/virtual/firma-a.com/info/');
INSERT INTO virtual_aliases (domain_id, source, destination)
VALUES (1, '[email protected]', '[email protected]');
Exim yapılandırmasında MySQL sorgularını kullanmak için hide mysql_servers direktifini ayarlamamız gerekiyor:
# /etc/exim4/conf.d/main/00_local_macros içine ekle
hide mysql_servers = localhost/mailserver/exim_user/guclu_parola
# Domain kontrolü için macro
VIRTUAL_DOMAIN_QUERY = SELECT domain FROM virtual_domains
WHERE domain='$domain' AND active=1
# Kullanıcı maildir sorgusu
VIRTUAL_USER_QUERY = SELECT maildir FROM virtual_users
WHERE email='$local_part@$domain' AND active=1
# Alias sorgusu
VIRTUAL_ALIAS_QUERY = SELECT destination FROM virtual_aliases
WHERE source='$local_part@$domain' AND active=1
MySQL destekli router tanımı:
# MySQL tabanlı virtual user router
mysql_virtual_user_router:
driver = redirect
domains = mysql;SELECT domain FROM virtual_domains WHERE domain='$domain' AND active=1
data = ${lookup mysql{SELECT maildir FROM virtual_users
WHERE email='${quote_mysql:$local_part}@${quote_mysql:$domain}'
AND active=1}}
file_transport = virtual_maildir_transport
no_more
# MySQL tabanlı alias router
mysql_alias_router:
driver = redirect
domains = mysql;SELECT domain FROM virtual_domains WHERE domain='$domain' AND active=1
data = ${lookup mysql{SELECT destination FROM virtual_aliases
WHERE source='${quote_mysql:$local_part}@${quote_mysql:$domain}'
AND active=1}}
no_more
Kota Yönetimi
Kullanıcılara disk kotası uygulamak özellikle paylaşımlı hosting ortamlarında zorunludur. Exim, transport seviyesinde kota kontrolü yapabilir:
virtual_maildir_transport:
driver = appendfile
maildir_format = true
create_directory = true
directory = ${lookup mysql{SELECT maildir FROM virtual_users
WHERE email='${quote_mysql:$local_part}@${quote_mysql:$domain}'}}
quota = ${lookup mysql{SELECT quota FROM virtual_users
WHERE email='${quote_mysql:$local_part}@${quote_mysql:$domain}'}}{$value}fail
quota_warn_threshold = 80%
quota_size_regex = d+ octets used,
delivery_date_add
envelope_to_add
return_path_add
group = vmail
user = vmail
mode = 0600
Kota dolduğunda gönderene otomatik bounce mesajı gider. Kullanıcının kota kullanımını izlemek için basit bir script yazalım:
#!/bin/bash
# /usr/local/bin/check_mailbox_quota.sh
MAIL_BASE="/var/mail/virtual"
WARN_THRESHOLD=80
find "$MAIL_BASE" -mindepth 2 -maxdepth 2 -type d | while read mailbox; do
domain=$(basename $(dirname "$mailbox"))
user=$(basename "$mailbox")
size=$(du -sb "$mailbox" 2>/dev/null | awk '{print $1}')
# MySQL'den kotayı çek
quota=$(mysql -u exim_user -pguclu_parola mailserver -se
"SELECT quota FROM virtual_users WHERE email='${user}@${domain}' AND active=1" 2>/dev/null)
if [ -n "$quota" ] && [ "$quota" -gt 0 ]; then
percent=$((size * 100 / quota))
if [ "$percent" -ge "$warn_threshold" ]; then
echo "UYARI: ${user}@${domain} kotasinin %${percent}'ini kullaniyor ($(numfmt --to=iec $size) / $(numfmt --to=iec $quota))"
fi
fi
done
Güvenlik Ayarları ve En İyi Pratikler
Sanal domain yapılandırmasında güvenlik kritik öneme sahip. Dikkat etmeniz gereken noktalar:
- Header sanitization: Gelen ve giden postalarda zararlı header’ları temizleyin
- Rate limiting: Tek bir adresten gelen bağlantı sayısını sınırlandırın
- Relay koruması: Yalnızca yetkili kullanıcıların relay yapabilmesini sağlayın
# ACL (Access Control List) yapılandırması
# /etc/exim4/conf.d/acl/30_exim4-config_check_rcpt
acl_check_rcpt:
# Yerel teslimat için sanal domain kontrolü
accept
domains = +virtual_domains
condition = ${lookup mysql{SELECT COUNT(*) FROM virtual_users
WHERE email='${quote_mysql:$local_part}@${quote_mysql:$domain}'
AND active=1}{$value}{0}}
condition = ${if >{$value}{0}{yes}{no}}
# Alias kontrolü
accept
domains = +virtual_domains
condition = ${lookup mysql{SELECT COUNT(*) FROM virtual_aliases
WHERE source='${quote_mysql:$local_part}@${quote_mysql:$domain}'
AND active=1}{$value}{0}}
condition = ${if >{$value}{0}{yes}{no}}
# Bilinmeyen kullanıcıları reddet
deny
domains = +virtual_domains
message = Unknown user $local_part@$domain
deny
message = relay not permitted
Exim’i Yeniden Başlatmak ve Test Etmek
Yapılandırma değişikliklerinden sonra Exim’i doğru şekilde yeniden yüklemek önemlidir:
# Yapılandırmayı syntax kontrolünden geçir
exim4 -bV
exim -C /etc/exim4/exim4.conf -bV 2>&1 | head -20
# Debian'da yapılandırmayı derle ve yeniden başlat
update-exim4.conf
systemctl restart exim4
# Belirli bir adresin nasıl yönlendirileceğini test et
exim -bt [email protected]
exim -bt [email protected]
# Test maili gönder
echo "Test mesaji" | exim -v [email protected]
# Log'u izle
tail -f /var/log/exim4/mainlog | grep -E "(firma-a|firma-b)"
exim -bt komutu özellikle troubleshooting için çok değerlidir. Hangi router’ın devreye girdiğini ve postanın nereye teslim edileceğini açıkça gösterir.
Sık Karşılaşılan Sorunlar ve Çözümleri
“Unknown user” hatası: Bu genellikle kullanıcı dosyasında yazım hatası olduğunda ya da MySQL sorgusunun boş döndüğünde yaşanır.
# Kullanıcı dosyasını doğrula
exim -d+lookup -bt [email protected] 2>&1 | grep -A5 "lookup"
# MySQL bağlantısını test et
exim -d+mysql -bt [email protected] 2>&1 | tail -20
“Permission denied” hatası: Posta kutusu dizinine yazma izni olmadığında ortaya çıkar.
# İzinleri kontrol et
ls -la /var/mail/virtual/firma-a.com/
# Gerekirse düzelt
chown -R vmail:vmail /var/mail/virtual/
chmod 750 /var/mail/virtual/firma-a.com/info/
“Quota exceeded” hataları düzgün çalışmıyor: Maildir kota hesaplaması için maildirsize dosyasının varlığını kontrol edin.
# Maildirsize dosyasını manuel oluştur veya yenile
maildirmake --quota 100000000S /var/mail/virtual/firma-a.com/info/
Domain yönlendirme döngüsü: Alias tanımları yanlış yapılandırıldığında döngü oluşabilir.
# Mesaj ID'si ile log'da döngü ara
grep "routing loop" /var/log/exim4/mainlog | tail -20
Performans Optimizasyonu
Yüksek trafikli ortamlarda birkaç optimizasyon önemlidir:
- DNS cache:
dns_ipv4_lookupvedns_again_means_nonexistparametrelerini ayarlayın - MySQL bağlantı havuzu: Exim her lookup için yeni bağlantı açmaz, ancak bağlantı timeout’larına dikkat edin
- Delivery parallelism:
remote_max_parallelvelocal_max_paralleldeğerlerini yükünüze göre ayarlayın
# /etc/exim4/conf.d/main/02_optimizasyon
deliver_queue_load_max = 10
queue_run_max = 5
remote_max_parallel = 20
local_max_parallel = 10
dns_retrans = 5s
dns_retry = 2
Kuyruk durumunu düzenli izlemek için:
# Kuyruk boyutunu göster
exim -bpc
# Kuyrukta bekleyen mesajları listele
exim -bp | head -40
# Takılı kalmış mesajları zorla yeniden dene
exim -qff
Sonuç
Exim ile sanal domain yönetimi başlangıçta karmaşık görünse de sistemin mantığını kavradıktan sonra son derece esnek ve güçlü bir yapıya sahip olduğunu görürsünüz. Metin tabanlı kullanıcı dosyaları küçük kurulumlar için ideal olurken, büyük ortamlarda MySQL entegrasyonu kaçınılmaz hale gelir.
Bu yazıda anlattığımız yapının özeti şöyle:
- Domain listesini dinamik bir dosyadan veya MySQL’den yönetin
- Her domain için ayrı kullanıcı ve alias tanımlamaları yapın
- Router’ları doğru sırayla dizin ve daha spesifik kuralları önce koyun
- ACL ile bilinmeyen kullanıcılara gelen postaları erken reddedin, böylece gereksiz işlem yapmayın
- Kota yönetimini transport seviyesinde uygulayın
exim -btve debug modunu troubleshooting’in ilk adımı olarak kullanın
Bir sonraki adım olarak TLS şifreleme, DKIM imzalama ve SpamAssassin entegrasyonunu ele alacağız. Mail sunucusu yönetimi bir bütündür ve sanal kullanıcı yönetimi bu bütünün temel taşını oluşturur.