# Yeni Sunucu Nasıl Güvenli Hale Getirilir? Adım Adım Rehber

> Sunucunun IP'si hiçbir yerde yayınlanmamıştı, alan adı bağlı değildi, site yayında değildi. İlk saatte 54 başarısız SSH giriş denemesi kaydedildi.

- Kanonik URL: https://algoritmaajans.com/tr/blog/yeni-sunucu-nasil-guvenli-hale-getirilir
- Dil: tr
- Kategori: Yazılım
- Yayın: 2026-08-27
- Güncelleme: 2026-08-29
- Yayıncı: Algoritma Ajans

---

**Yeni bir sunucuyu güvenli hale getirmenin özü beş işte toplanıyor: şifreyle SSH girişini kapatıp anahtara geçmek, güvenlik duvarını yalnızca gereken portlara daraltmak, `fail2ban` ile ısrarcı IP'leri engellemek, uygulamayı root olmayan bir kullanıcıyla çalıştırmak ve güvenlik güncellemelerini otomatiğe bağlamak. Toplam yirmi-otuz dakika.**

Yeni bir VPS kurduk. Kurulum bitti, güvenlik duvarını yapılandırdık, `fail2ban`'i açtık. Bir saat sonra durumunu kontrol ettik:

```
Status for the jail: sshd
|- Currently failed:  1
|- Total failed:      54
`- Currently banned:  1
```

**54 başarısız SSH giriş denemesi.** Sunucunun IP adresi henüz hiçbir yerde yayınlanmamıştı. Alan adı bağlı değildi. Site yayında değildi.

Kimse bizi hedef almamıştı. Sadece internet böyle çalışıyor: yeni açılan her IP, otomatik tarayıcılar tarafından dakikalar içinde bulunur ve denenir.

## Ne aradıklarını biliyoruz

Bu tarayıcılar akıllı değil. Basitçe şunları dener:

- `root` kullanıcısıyla yaygın şifreler (`123456`, `admin`, `password`, `root`)
- Sık kullanılan kullanıcı adları (`admin`, `ubuntu`, `test`, `git`, `postgres`)
- Sızmış şifre veri tabanlarındaki kombinasyonlar

Şifreyle SSH girişi açıksa ve şifre zayıfsa, bu iş saatler içinde biter. Sunucunuz kripto madenciliği ya da spam gönderimi için kullanılmaya başlar ve genelde aylarca fark edilmez.

Kendi sunucunuzda ne olduğunu görmek isterseniz:

```bash
journalctl -u sshd --since "24 hours ago" \
  | grep -oE 'Invalid user [a-z0-9_-]+' | sort | uniq -c | sort -rn | head -20
```

Çıktı denenen kullanıcı adlarını sıklık sırasına dizer. Listede `admin`, `test`, `oracle`, `ubuntu`, `git`, `ftpuser` gibi isimleri göreceksiniz — bu bir sözlük, size özel bir şey değil.

Bir de nereden geldiklerine bakın:

```bash
journalctl -u sshd --since "24 hours ago" \
  | grep -oE 'from [0-9.]+' | sort | uniq -c | sort -rn | head -10
```

Genelde onlarca farklı IP çıkar. Tek tek engellemenin anlamsız olmasının sebebi de bu — ertesi gün başka IP'ler gelir. Engellenmesi gereken şey IP değil, **yöntem**: şifreyle giriş.

## Kurulumda ilk yapılacak beş iş

Sırayla, sunucuyu kurar kurmaz.

### 1. Şifreyle girişi tamamen kapatın

Şifre ne kadar güçlü olursa olsun denenebilir. SSH anahtarı denenemez.

Kendi makinenizde anahtar üretin:

```bash
ssh-keygen -t ed25519 -f ~/.ssh/sunucum -C "sunucum-$(date +%Y%m%d)"
```

`ed25519` tercih edin; RSA'ya göre daha kısa, daha hızlı ve en az onun kadar güçlü. Anahtara bir parola verin — anahtar dosyanız çalınırsa parola tek savunmanız olur. Her seferinde yazmamak için `ssh-agent` kullanın:

```bash
ssh-add ~/.ssh/sunucum
```

Genel anahtarı sunucuya kopyalayın:

```bash
ssh-copy-id -i ~/.ssh/sunucum.pub root@sunucu
```

`ssh-copy-id` yoksa elle: `~/.ssh/sunucum.pub` içeriğini sunucudaki `~/.ssh/authorized_keys` dosyasına ekleyin. Dosya izinleri önemli — yanlışsa SSH anahtarı sessizce yok sayar:

```bash
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
```

Sonra şifreli girişi kapatın:

```
# /etc/ssh/sshd_config.d/99-sikilastirma.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
PermitEmptyPasswords no
MaxAuthTries 3
LoginGraceTime 20
```

`prohibit-password`, root'un yalnızca anahtarla girebileceği anlamına gelir. `MaxAuthTries` ve `LoginGraceTime` ise bir denemenin ne kadar süreceğini kısaltır; kaba kuvvet zaten işe yaramayacak ama sunucunuz boşuna meşgul olmaz.

Ayrı bir dosyaya (`sshd_config.d/` altına) yazmanın sebebi: ana `sshd_config` dosyası paket güncellemelerinde değişebilir, sizin dosyanız değişmez.

**Uygulamadan önce mutlaka test edin:**

```bash
sshd -t                    # yapılandırma geçerli mi
systemctl reload sshd      # uygula
```

Sonra **yeni bir terminalde** bağlanmayı deneyin. Mevcut oturumunuzu kapatmayın — anahtar çalışmıyorsa geri dönecek yolunuz kalmaz.

Kapandığını doğrulayın:

```bash
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no root@sunucu
# Permission denied (publickey) → doğru
```

**SSH portunu değiştirmeli miyim?** Tartışmalı bir konu. 22 yerine 2222 kullanmak otomatik tarayıcıların çoğunu eler ve loglarınızı belirgin biçimde temizler. Ama gerçek bir güvenlik katmanı değildir — port taraması yapan biri onu da bulur. Anahtar zorunluysa asıl korumayı zaten kurmuşsunuzdur. Biz 22'de kaldık; log gürültüsü sizi rahatsız ediyorsa değiştirin, "güvenlik için şart" diye düşünmeyin.

### 2. Güvenlik duvarını daraltın

Varsayılan kurulumlarda gereksiz servisler açık gelir. Bizim sunucumuzda `cockpit` (web tabanlı yönetim paneli) açıktı ve kullanmıyorduk.

```bash
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --permanent --remove-service=cockpit
firewall-cmd --reload
firewall-cmd --list-services
```

Ubuntu/Debian'daysanız karşılığı `ufw`:

```bash
ufw default deny incoming
ufw default allow outgoing
ufw allow OpenSSH
ufw allow 80,443/tcp
ufw enable
```

Kural basit: kullanmadığınız her açık port fazladan risktir.

Bir de dışarıdan doğrulayın — sunucunun kendi üzerinden bakmak yanıltıcıdır, çünkü yerel arayüz farklı davranır:

```bash
# Kendi makinenizden
nmap -Pn -p 1-1024 SUNUCU_IP
```

Açık görünen tek portlar 22, 80 ve 443 olmalı. Veritabanı portu (3306, 5432) ya da bir uygulama portu (3000, 8080) dışarıdan görünüyorsa acil kapatın. Uygulamanızı `127.0.0.1` üzerinde dinletip önüne nginx koymak, güvenlik duvarına güvenmekten daha sağlam bir çözüm.

### 3. fail2ban

Anahtar zorunlu olduğunda kaba kuvvet zaten işe yaramaz, ama `fail2ban` yine de değerli: sürekli deneyen IP'leri engelleyerek log'larınızı temiz tutar ve sunucunun boşuna işlem yapmasını önler.

```ini
# /etc/fail2ban/jail.local
[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 5
backend  = systemd
ignoreip = 127.0.0.1/8 ::1 SIZIN_OFIS_IPNIZ

[sshd]
enabled = true

[recidive]
enabled  = true
bantime  = 1w
findtime = 1d
maxretry = 5
```

`recidive` hapishanesi, defalarca yasaklanan IP'leri bir haftalığına engeller. Aynı IP'nin her saat başı geri gelmesini bitirir.

`ignoreip` satırına kendi sabit IP'nizi yazın. Sabit IP'niz yoksa yazmayın — dinamik bir IP'yi güvenli listeye almak, o IP başkasına geçtiğinde onu güvenli listeye almak demek.

```bash
systemctl enable --now fail2ban
fail2ban-client status sshd
```

**Kendinizi yasaklarsanız** — olur, özellikle anahtar testleri sırasında — sunucu sağlayıcınızın konsolundan girip kaldırın:

```bash
fail2ban-client set sshd unbanip SIZIN_IPNIZ
```

Konsol erişiminizin olduğunu **anahtarları kapatmadan önce** doğrulayın. netcup, Hetzner, DigitalOcean gibi sağlayıcıların hepsinde VNC ya da seri konsol var; nerede olduğunu ihtiyacınız olduğunda değil, şimdi öğrenin.

### 4. Uygulamayı root ile çalıştırmayın

Web uygulamanız kendi kullanıcısıyla çalışmalı. Uygulamada bir açık bulunursa saldırgan yalnızca o kullanıcının yetkisini ele geçirir, sunucunun tamamını değil.

```bash
useradd --system --create-home --home-dir /var/lib/uygulamam --shell /sbin/nologin uygulamam
```

`--shell /sbin/nologin` önemli: bu kullanıcıyla kimse SSH ile giremez, sadece servis çalışır.

systemd birimi yazıyorsanız sıkılaştırma seçeneklerini de ekleyin:

```ini
[Service]
User=uygulamam
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/var/www/uygulamam/veri
ProtectHome=true
ProtectKernelTunables=true
ProtectControlGroups=true
RestrictSUIDSGID=true
LockPersonality=true
```

`ProtectSystem=strict` dosya sistemini salt okunur yapar; uygulama yalnızca `ReadWritePaths` ile açıkça izin verdiğiniz klasörlere yazabilir. `NoNewPrivileges=true` ise süreç ağacının sonradan yetki yükseltmesini engeller — bir açık bulunsa bile saldırganın root'a tırmanma yolunu kapatır.

Ne kadar sıkılaştırdığınızı systemd'nin kendi puanıyla ölçebilirsiniz:

```bash
systemd-analyze security uygulamam.service
```

Çıktı 0-10 arası bir "exposure" değeri verir; düşük olan iyidir. Her satırı düzeltmeye çalışmayın, ama 9 üzeri bir puan görüyorsanız yukarıdaki listeyi hiç uygulamamışsınız demektir.

**Bir uyarı:** uygulama dizini kullanıcının ev dizini olmamalı. `ProtectHome=true` ev dizinlerini servise kapatır ve uygulamanız kendi klasörünü göremez. Biz bu hatayı yaptık; belirtisi şuydu:

```
Failed to load environment files: Permission denied
Failed with result 'resources'
```

Çözüm: kullanıcının ev dizinini ayrı bir yere alın (`/var/lib/uygulamam`), uygulama `/var/www/uygulamam` altında dursun.

### 5. Güvenlik güncellemelerini otomatiğe bağlayın

Dördünü de yaptınız ve altı ay sonra kritik bir OpenSSH açığı çıktı. Kimse `dnf update` çalıştırmadıysa, yaptığınız her şey o açığın arkasında kalır.

AlmaLinux / Rocky / RHEL tarafında:

```bash
dnf install -y dnf-automatic
# /etc/dnf/automatic.conf içinde:
#   upgrade_type = security
#   apply_updates = yes
systemctl enable --now dnf-automatic.timer
```

Ubuntu / Debian tarafında:

```bash
apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
```

`upgrade_type = security` bilinçli bir seçim: yalnızca güvenlik yamalarını uygular, sürüm atlamalarını uygulamaz. Otomatik güncellemenin bir gece uygulamanızı bozması, güncellenmemiş bir sunucudan daha az olası ama sıfır değil — bu ayar riski en aza indirir.

Çekirdek güncellemeleri yeniden başlatma ister. Otomatik yeniden başlatma açmak yerine ayda bir bakıp elle yapmak daha kontrollü:

```bash
needs-restarting -r   # "Reboot is required" diyorsa planlayın
```

## SELinux'u kapatmayın

AlmaLinux, Rocky ya da RHEL kullanıyorsanız SELinux varsayılan olarak açıktır ve bir noktada size zorluk çıkarır. İnternetteki çözümlerin çoğu `setenforce 0` der. Yapmayın.

Bizim karşılaştığımız sorun şuydu: systemd, `/var/www` altındaki ortam dosyasını okuyamıyordu. Üstelik **hiçbir log kaydı yoktu** — SELinux'ta bazı redler "dontaudit" kuralıyla sessizce yapılır.

Teşhis için geçici olarak izin kipine almak meşrudur:

```bash
setenforce 0          # test
systemctl restart uygulamam
setenforce 1          # HEMEN geri aç
```

Servis izin kipinde çalışıyorsa sebep SELinux'tur. Çözüm doğru bağlamı vermektir, korumayı kapatmak değil.

Sessiz redleri görünür kılmak için `dontaudit` kurallarını geçici olarak devre dışı bırakabilirsiniz:

```bash
semodule -DB              # dontaudit kapalı
# sorunu tekrarlayın
ausearch -m AVC -ts recent
semodule -B               # geri aç
```

Bizim çözümümüz basitti: ortam dosyasını `/etc/uygulamam/` altına taşımak. `/etc` altındaki `etc_t` bağlamını systemd okuyabiliyor, `/var/www`'daki `httpd_sys_content_t` bağlamını okuyamıyor.

Servisin ağ bağlantısı kurması gerekiyorsa (nginx'ten Node'a proxy gibi):

```bash
setsebool -P httpd_can_network_connect 1
```

## Yedek: güvenliğin atlanan halkası

Güvenlik yalnızca "içeri girmesinler" değil, "girerlerse ne kaybederim" sorusudur. Fidye yazılımı ya da basit bir insan hatası karşısında sizi kurtaran şey yedektir.

Üç kural:

**1. Yedek başka bir makinede dursun.** Aynı sunucudaki yedek, sunucu ele geçirildiğinde ele geçirilmiş demektir.

**2. Geri yüklemeyi deneyin.** Denenmemiş yedek, yedek değildir. Ayda bir, yedeği boş bir dizine açıp veritabanının okunabildiğini doğrulayın.

**3. Sürüm tutun.** Tek bir "son yedek" varsa, bozulmuş veriyi fark etmeden yedeklemişsinizdir. Günlük yedeklerin bir haftalığını, haftalıkların bir aylığını saklayın.

SQLite kullanıyorsanız dosyayı kopyalamak yetmez — yazma sırasında kopyalanan dosya bozuk çıkabilir:

```bash
sqlite3 /var/lib/uygulamam/veri.db ".backup '/yedek/veri-$(date +%F).db'"
```

Bu komut tutarlı bir kopya üretir. Müşteri verisi, cari kayıt ya da fatura tutan bir sistem işletiyorsanız bu konunun operasyonel tarafını [ön muhasebe programı yazısında](/tr/blog/on-muhasebe-programi-nasil-secilir) veri sahipliği başlığı altında ayrıca ele aldık.

## Bir hafta sonra kontrol

```bash
fail2ban-client status sshd        # kaç deneme, kaç yasak
journalctl -u sshd --since "7 days ago" | grep -c "Failed"
firewall-cmd --list-all            # beklenmedik port açılmış mı
systemctl list-units --failed      # çöken servis var mı
last -n 20                         # kim, ne zaman girdi
```

Rakamlar yüksek çıkacak. Bu normal — engellendikleri sürece sorun değil.

`last` çıktısına dikkat edin: orada sizin bilmediğiniz bir oturum varsa rakamlar artık normal değildir. Aynı şey `journalctl -u sshd | grep "Accepted"` için de geçerli — kabul edilen her giriş sizin olmalı.

## Ele geçirildiğinizi nasıl anlarsınız

Başarılı bir saldırının belirtisi hata mesajı değil, **açıklanamayan normallik bozukluğu** olur. Dört gösterge:

**CPU sürekli meşgul.** Kripto madenciliği en yaygın kullanım. `top` çıktısında tanımadığınız bir süreç %90 CPU tüketiyorsa bakın. Süreç adları masum görünür (`kdevtmpfsi`, `dbused`, `sysupdate` gibi).

```bash
top -b -n1 -o %CPU | head -15
```

**Giden trafikte artış.** Spam gönderimi ya da başka bir siteye saldırı için kullanılıyor olabilirsiniz. Sağlayıcınızın panelindeki trafik grafiği ilk bakılacak yer.

**Bilmediğiniz cron kayıtları.** Kalıcılık en sık buradan sağlanır:

```bash
crontab -l; for k in $(cut -d: -f1 /etc/passwd); do crontab -u $k -l 2>/dev/null; done
ls -la /etc/cron.d/ /etc/cron.daily/
```

**`authorized_keys` içinde tanımadığınız anahtar.** En sinsi kalıcılık yöntemi; şifreyi değiştirseniz bile erişim devam eder.

```bash
for f in /root/.ssh/authorized_keys /home/*/.ssh/authorized_keys; do
  echo "── $f"; cat "$f" 2>/dev/null
done
```

Her satırın sonundaki yorum alanına bakın; oradaki isim size ait değilse sorununuz var.

Ele geçirildiğinden şüpheleniyorsanız temizlemeye çalışmayın. Kalıcılık mekanizmalarının hepsini bulduğunuzdan asla emin olamazsınız. Doğru yol: veriyi yedekten alıp **sıfırdan yeni bir sunucu kurmak** ve bu yazıdaki adımları bu kez ilk gün uygulamak.

## Sık yapılan beş hata

**1. Anahtarı test etmeden mevcut oturumu kapatmak.** En pahalı hata. Sunucu sağlayıcınızın konsolu yoksa sunucuyu sıfırlamak zorunda kalırsınız.

**2. Uygulamayı root ile çalıştırmak.** "Sonra düzeltirim" denen ve düzeltilmeyen madde.

**3. Veritabanı portunu dışarı açmak.** Yerel bir araçtan bağlanmak için 3306'yı açmak yerine SSH tüneli kullanın: `ssh -L 3306:127.0.0.1:3306 sunucu`.

**4. SELinux'u kapatmak.** Bir sorunu çözer, koruma katmanını tamamen kaldırır.

**5. `.env` dosyasını dünyaya okunur bırakmak.** `chmod 600` verin ve sahibini servis kullanıcısı yapın. İçinde veritabanı şifresi ve API anahtarları var.

## Yirmi dakikalık kontrol listesi

- [ ] SSH anahtarı üretildi ve sunucuya kopyalandı
- [ ] `PasswordAuthentication no` uygulandı ve **yeni bir terminalde** test edildi
- [ ] Sağlayıcı konsoluna (VNC / seri) erişimin çalıştığı doğrulandı
- [ ] Güvenlik duvarında yalnızca 22, 80, 443 açık
- [ ] Dışarıdan port taraması yapıldı, sürpriz yok
- [ ] `fail2ban` çalışıyor, `recidive` hapishanesi açık
- [ ] Uygulama root olmayan bir kullanıcıyla çalışıyor
- [ ] systemd biriminde sıkılaştırma satırları var
- [ ] Otomatik güvenlik güncellemesi açık
- [ ] Yedek başka bir makinede ve geri yükleme denendi
- [ ] `.env` dosyası `600` izniyle ve doğru sahiple duruyor

## Özet

Sunucunuz internete bağlandığı andan itibaren taranıyor. Sizi hedef alan kimse yok; sadece otomatik araçlar bütün IP aralığını deniyor.

Bunu bir saldırı olarak değil, hava durumu olarak düşünün. Yağmur yağacak; siz çatıyı kurun.

Beş adım, toplam yarım saat: anahtar zorunlu, güvenlik duvarı dar, `fail2ban` açık, uygulama root değil, güncellemeler otomatik. Bu beşi, gerçekleşen saldırıların büyük çoğunluğunu daha başlamadan bitirir.

Bu işleri kendi sunucunuzda yapabiliyor olmanız, [paylaşımlı hosting yerine VPS seçmenin](/tr/blog/paylasimli-hostinge-nextjs-kurulur-mu) en somut getirisi — paylaşımlı bir hesapta ne güvenlik duvarına ne systemd'ye erişiminiz olur. Sunucu hazır olduktan sonraki adım alan adını doğru bağlamak; [Cloudflare geçişinde](/tr/blog/cloudflare-dns-gecisi-nasil-yapilir) hangi kayıtların kaybolduğunu ayrı bir yazıda anlattık.

Müşteri verisi ve sipariş tutan bir sistem kuruyorsanız bu adımlar isteğe bağlı değil; [e-ticaret sitesi nasıl kurulur yazısındaki](/tr/blog/e-ticaret-sitesi-nasil-kurulur) altyapı bölümü aynı listeyi ticari tarafla birlikte ele alıyor. Sunucu kurulumunun bir projenin bütçesinde nereye düştüğünü ise [web sitesi fiyatları yazısında](/tr/blog/web-sitesi-fiyatlari-neye-gore-degisir) ayırdık.

<!-- GORSEL: sunucu-ilk-saat-inline-1 — fail2ban durum çıktısının terminal görünümü: 54 başarısız deneme, 1 yasaklı IP; kenarında ilk saat vurgusu -->
