BT Sistem Yönetimi

Linux TFTP Sunucusu Kurulumu: tftpd-hpa Adım Adım

Ubuntu'da tftpd-hpa ile TFTP sunucusu kurulumu: --create, --secure ve --umask, dinlenen adres ve varsayılan kurulumun ilk yedeği neden almadığı, lab'da ölçüldü.

İlker Pehlivan

Linux’ta TFTP sunucusu kurmak tek pakettir, ama o paketin varsayılan hâli bir cihazın ilk yedeğini almaz, aldığı yedekleri de herkese açık bırakır. Bu yazıda Ubuntu Server üzerinde tftpd-hpa’yı bir istemciyle sınadım: dosya okuttum, yazdırdım, dizinin dışına çıkmayı denedim ve her adımda sunucunun ağa ne cevap verdiğini dinledim. Ölçümlerden çıkan yapılandırma, kutudan çıkan yapılandırmadan üç bayrak farklı.

Bir ofisin fotokopi odasını düşünün. Odayı kurmak kolaydır: bir masa, bir makine, bir kapı. Zor olan, çıkan kâğıtların kimin eline geçeceğine ve odanın binanın neresinde duracağına karar vermektir. TFTP sunucusu da böyledir: kurulumu birkaç satır, asıl iş sunucunun kime açık olduğudur.

Linux TFTP sunucusu kurulumu benzetmesi: bir teknisyen ofisin fotokopi odasında yeni makineyi kuruyor, makinenin yan panelindeki tek kolu açık konuma getiriyor, odanın kapısı koridora açık
Fotokopi odasını kurmak tek bir kolu açmaya bakar; asıl iş, çıkan kâğıtların kimin eline geçeceğidir. TFTP sunucusu kurmak da böyledir.

Bu yazı Ubuntu Server rehberimizin bir rol uygulamasıdır ve BT Sistem Yönetimi hizmetimizin bir parçasıdır. TFTP protokolünün ne olduğunu, port 69’da neden kimlik sormadığını ve bir aktarımın paket paket nasıl aktığını TFTP nedir yazımızda anlattık; burada konu, o protokolü konuşan sunucuyu Linux’ta kurmak ve doğru ayarlamaktır.

Başlamadan Önce: Ubuntu Server ve Sunucunun Yeri

Bu rehber, üzerine TFTP sunucusu kuracağınız Ubuntu Server’ı zaten ayağa kaldırdığınızı varsayar. İşletim sistemi kurulumu, disk düzeni ve ilk yapılandırma ayrı bir konudur ve Ubuntu Server kurulum rehberimizde ele alınmaktadır.

Kurulumdan önce bir karar gerekir: bu sunucu ağın neresinde duracak? TFTP kimlik sormadığı için sunucuyu görebilen her cihaz ondan dosya okuyabilir. Bu yüzden TFTP sunucusunun yeri, yalnız ağ cihazlarının yönetim trafiğinin dolaştığı ağdır. Lab’da bu rolü merkezi syslog sunucusu olarak kullandığım Ubuntu 24.04 makinesi üstleniyor; ağ cihazlarının yönetim trafiği zaten oraya akıyor.

TFTP Sunucusu Kurulumu: Tek Paket, Tek Yapılandırma Dosyası

Ubuntu’da sunucu tarafı tek pakettir:

Bash
sudo apt install tftpd-hpa

Kurulumun bütün ayarı /etc/default/tftpd-hpa dosyasındadır. Lab sunucumdaki dosya şu:

Yapılandırma
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure --create"

Bu dosya kutudan çıkan hâl değil. TFTP nedir yazısı için paketi kurduğumda TFTP_OPTIONS satırında yalnız --secure vardı; --create’i ben ekledim. Nedenini aşağıda ölçeceğim.

Dosyadaki satırların sunucuya nasıl geçtiğini görmek için çalışan süreci listeledim:

Bash
$ ps -o user=,cmd= -C in.tftpd
root     /usr/sbin/in.tftpd --listen --user tftp --address :69 --secure --create /srv/tftp

Dosyadaki her satır, komuttaki sırasıyla birebir bir argümana dönüşmüş:

  1. TFTP_USERNAME → --user tftp
  2. TFTP_ADDRESS → --address :69
  3. TFTP_OPTIONS olduğu gibi → --secure --create
  4. TFTP_DIRECTORY en sonda → /srv/tftp

Dinleyen süreç root olarak çalışıyor, çünkü port 69 ayrıcalıklı bir port. Paketin kılavuzu --user bayrağını “Specify the username which tftpd will run as” diye tanımlıyor; lab’da istemcinin yüklediği her dosya tftp:tftp sahipliğinde oluştu.

Bir ayrıntı da servis durumunda görünüyor. tftpd-hpa Ubuntu 24.04’te hâlâ eski tip bir başlatma betiği olarak geliyor ve systemd onu sarmalıyor:

Bash
$ systemctl is-enabled tftpd-hpa
tftpd-hpa.service is not a native service, redirecting to systemd-sysv-install.
Executing: /usr/lib/systemd/systemd-sysv-install is-enabled tftpd-hpa
enabled

Bu ayrıntı sorun giderme bölümünde önem kazanacak: böyle sarmalanan bir serviste systemctl’in “çalışıyor” demesi, sunucunun gerçekten dinlediği anlamına gelmiyor.

TFTP Sunucusu İlk Yedeği Neden Almıyor? —create ve Sahiplik

TFTP nedir yazısı için kurulumun standart tarifini lab switch’imde birebir uyguladım: paketi kur, switch’ten copy komutunu çalıştır, yedek gelsin. İlk denemede yedek gelmedi ve switch File transfer failed! dedi. Sebep iki eksikti:

  • --create yoktu. Paketin kılavuzu bu bayrağı şöyle anlatıyor: “Allow new files to be created. By default, tftpd will only allow upload of files that already exist.” Bir cihaz ilk yedeğini gönderdiğinde o dosya sunucuda henüz yoktur, dolayısıyla yazma reddedilir.
  • /srv/tftp dizini root:nogroup sahipliğindeydi. Dosyaları tftp kullanıcısı yazdığı için bu kullanıcı dizine dosya açamaz.

Bu yazı için --create’in etkisini yeniden ölçtüm. Bayrağı kaldırıp yeni bir dosya yüklemeyi denedim:

Bash
$ curl -s -T /tmp/fail.txt tftp://127.0.0.1/olmayan-dosya.txt; echo "yazma curl çıkış: $?"
yazma curl çıkış: 68

curl’ün 68 kodu, TFTP sunucusunun dosyayı bulamadığı anlamına geliyor. Dosya oluşmadı. Asıl öğretici olan sunucu tarafıydı: journalctl -u tftpd-hpa çıktısında yalnız servisin yeniden başlama satırları vardı; reddedilen yazmaya dair tek satır yoktu.

Reddin kendisi yalnız ağ trafiğinde yazılıydı. Denemeyi bu kez ağı dinleyerek tekrarladım; istek port 69’a gitti, cevap başka bir porttan döndü:

Çıktı
16:58:32.089137 IP 127.0.0.1.39573 > 127.0.0.1.69: TFTP, length 50, WRQ "olmayan.txt" octet tsize 2 blksize 512 timeout 1
16:58:32.091172 IP 127.0.0.1.45858 > 127.0.0.1.39573: UDP, length 19
	0x0000:  4500 002f 8cdb 0000 4011 efe0 7f00 0001  E../....@.......
	0x0010:  7f00 0001 b322 9a95 001b fe2e 0005 0001  ....."..........
	0x0020:  4669 6c65 206e 6f74 2066 6f75 6e64 00    File.not.found.

İkinci paketin veri kısmı 0005 0001 ile başlıyor ve üç parçadan oluşuyor:

  1. 0005, TFTP’nin hata işlem kodudur.
  2. 0001, hata kodudur.
  3. File not found, düz metin hata iletisidir.

Sunucu bir yazma isteğine “dosya bulunamadı” diye cevap veriyor. Bu davranışın protokol tarafını ve switch’in bu cevabı nasıl gösterdiğini TFTP nedir yazımızda anlattık.

Çözüm iki eksiği birlikte kapatmaktır:

Bash
sudo sed -i 's|^TFTP_OPTIONS=.*|TFTP_OPTIONS="--secure --create"|' /etc/default/tftpd-hpa
sudo chown tftp:tftp /srv/tftp
sudo systemctl restart tftpd-hpa

--create açıkken aynı istemciden yeni bir dosya yüklediğimde yükleme geçti ve dosya /srv/tftp içinde oluştu:

Bash
$ curl -s -T /tmp/u.txt tftp://127.0.0.1/umasksiz.txt; echo "curl çıkış: $?"
curl çıkış: 0

TFTP Sunucusunda Yedek Dosyalarının İzni: 666, —umask ve —permissive

Yükleme geçti, ama oluşan dosyanın iznine baktım:

Bash
$ stat -c "%a %U:%G %n" /srv/tftp/umasksiz.txt
666 tftp:tftp /srv/tftp/umasksiz.txt

666, sunucudaki her kullanıcının dosyayı okuyup yazabileceği anlamına geliyor. Bu bir hata değil, belgelenmiş bir varsayılan. Kılavuz --create’in devamında şunu söylüyor: “Files are created with default permissions allowing anyone to read or write them, unless the —permissive or —umask options are specified.” Bir switch yedeği bu dizine düştüğünde içinde SNMP community string ve yönetici parolalarının hash’leri düz metin durur; bunu TFTP nedir yazımızda paket dökümünde gösterdik. Fotokopi odasının çıktı tepsisi, binadaki herkesin eline açık duruyor.

Kılavuzun işaret ettiği ilk çözüm --umask. Bayrağı 077 değeriyle ekleyip aynı yüklemeyi yaptım:

Bash
$ curl -s -m 10 -T /tmp/u.txt tftp://127.0.0.1/umask-tek.txt; echo "curl çıkış: $?"
curl çıkış: 69
$ stat -c "%a %U:%G %s bayt %n" /srv/tftp/umask-tek.txt
600 tftp:tftp 0 bayt /srv/tftp/umask-tek.txt

İzin 600’e indi, ama yükleme başarısız oldu ve geride boş bir dosya kaldı. Sunucunun cevabı sebebi düz metin söylüyordu:

Çıktı
18:22:43.955833 IP 127.0.0.1.41538 > 127.0.0.1.59357: UDP, length 44
	0x0000:  4500 0048 11a8 0000 4011 6afb 7f00 0001  E..H....@.j.....
	0x0010:  7f00 0001 a242 e7dd 0034 fe47 0005 0002  .....B...4.G....
	0x0020:  4669 6c65 206d 7573 7420 6861 7665 2067  File.must.have.g
	0x0030:  6c6f 6261 6c20 7772 6974 6520 7065 726d  lobal.write.perm
	0x0040:  6973 7369 6f6e 7300                      issions.

0005 0002, yani hata kodu 2: “File must have global write permissions.” tftpd-hpa yazılacak dosyanın herkese yazılabilir olmasını ayrıca denetliyor; umask dosyayı ondan önce kapattığı için kendi koyduğu kurala takılıyor. Geride kalan 0 baytlık dosya ayrıca tehlikeli: yalnız “yedek dosyası var mı?” diye bakan bir denetim bu dosyayı yedek sanar.

Bu ek denetimi kapatan bayrak --permissive. Kılavuz onu şöyle tanımlıyor: “Perform no additional permissions checks above the normal system-provided access controls for the user specified via the —user option.” İkisini birlikte verince:

Bash
$ ps -o user=,cmd= -C in.tftpd
root     /usr/sbin/in.tftpd --listen --user tftp --address :69 --secure --create --umask 077 --permissive /srv/tftp
$ curl -s -m 10 -T /tmp/u.txt tftp://127.0.0.1/umask-permissive.txt; echo "curl çıkış: $?"
curl çıkış: 0
$ stat -c "%a %U:%G %s bayt %n" /srv/tftp/umask-permissive.txt
600 tftp:tftp 7 bayt /srv/tftp/umask-permissive.txt

Yükleme geçti, dosya içeriğiyle geldi ve yalnız sahibi tarafından okunabilir. Yedek alan bir TFTP sunucusu için önerdiğim satır bu:

Yapılandırma
TFTP_OPTIONS="--secure --create --umask 077 --permissive"

--permissive’in bedeli, tftpd-hpa’nın kendi ek denetimlerini kapatmasıdır; bundan sonra erişimi yalnız dosya sistemi izinleri ve bir sonraki bölümdeki --secure sınırlar. Yedekleri bu dizinde bekletmeyip tarihli ve korumalı bir depoya taşımayı switch yapılandırma yedeği yazımızda anlattık.

TFTP Sunucusunda —secure: Sunucu Kendi Dizininin Dışını Görmez

--secure, kılavuzun ifadesiyle “a chroot() environment for the server to run in” kurar: bağlantı kurulduğunda sunucunun kök dizini /srv/tftp olur. Bunu sınamak için dizinin dışındaki /etc/hostname dosyasını okumaya çalıştım.

İlk denemede ölçüm aracının kendisi yanıldı. curl, adresteki ../ parçalarını isteği göndermeden kendisi temizliyor; ağda sunucuya giden isim etc/hostname oldu:

Bash
$ curl -s tftp://127.0.0.1/../../../etc/hostname; echo "curl çıkış: $?"
curl çıkış: 68
Çıktı
18:17:21.669909 IP 127.0.0.1.39090 > 127.0.0.1.69: TFTP, length 51, RRQ "etc/hostname" octet tsize 0 blksize 512 timeout 6

Bu deney --secure’ü sınamıyor, çünkü sunucu ../ görmedi. curl’ün --path-as-is seçeneği adresi olduğu gibi gönderiyor:

Bash
$ curl -s --path-as-is tftp://127.0.0.1/../../../etc/hostname; echo "curl çıkış: $?"
curl çıkış: 68
Çıktı
18:17:24.738693 IP 127.0.0.1.55129 > 127.0.0.1.69: TFTP, length 60, RRQ "../../../etc/hostname" octet tsize 0 blksize 512 timeout 6
18:17:24.740601 IP 127.0.0.1.55775 > 127.0.0.1.55129: UDP, length 19

Bu kez istek ../ ile sunucuya ulaştı ve sunucu yine File not found döndü. Mutlak yolla (/etc/hostname) sorduğumda da sonuç aynıydı. Dosya sistemde var, ama sunucu için dünya /srv/tftp’den ibaret. Bu bayrak her zaman açık kalmalı. Yine de --secure erişimi dizine hapseder, bir parola getirmez: o dizine ulaşan herkes içindekini okur.

TFTP Sunucusu Hangi Adresi Dinliyor? Varsayılanın Tuzağı

TFTP_ADDRESS=":69" satırında iki noktanın önüne bir adres yazılmamış; bu, “her adreste dinle” demektir. Sunucunun açtığı soketleri listeledim:

Bash
$ sudo ss -ulnp | grep -E ":69 |in.tftpd"
UNCONN 0      0                               0.0.0.0:69        0.0.0.0:*    users:(("in.tftpd",pid=1229,fd=4))
UNCONN 0      0                                  [::]:69           [::]:*    users:(("in.tftpd",pid=1229,fd=5))

Sunucu hem bütün IPv4 adreslerinde (0.0.0.0) hem de bütün IPv6 adreslerinde ([::]) port 69’u dinliyor. Sunucunun kullanıcı ağına ya da dışarıya bakan bir arayüzü varsa, TFTP orada da açık demektir. Dinlemeyi yönetim ağındaki adrese sabitlemek için iki noktanın önüne o adres yazılır:

Yapılandırma
TFTP_ADDRESS="10.34.10.20:69"

Lab sunucumun tek arayüzü olduğu için bu satırın farkını lab’da ölçmedim; yazım biçimi kılavuzdaki --address seçeneğinin biçimidir. Yönetim ağının neden ayrı tutulduğunu ve segmentler arasında hangi geçişin açık olacağını ağ segmentasyonu yazımızda anlattık.

TFTP Aktarımı Port 69’da Başlar, Başka Porttan Sürer: Firewall Tuzağı

TFTP’de aktarımın tamamı port 69 üzerinden yürümez. Başarılı bir okumayı dinledim:

Çıktı
16:57:33.083517 IP 127.0.0.1.41557 > 127.0.0.1.69: TFTP, length 49, RRQ "deneme.txt" octet tsize 0 blksize 512 timeout 6
16:57:33.085393 IP 127.0.0.1.40232 > 127.0.0.1.41557: UDP, length 33
16:57:33.085484 IP 127.0.0.1.41557 > 127.0.0.1.40232: UDP, length 4
...

İstek port 69’a gitti, sunucu ise 40232 numaralı yeni bir porttan cevap verdi ve aktarımın geri kalanı bu port üzerinden yürüdü. Port 69 yalnız ilk paketi görüyor.

Bunun iki pratik sonucu var. Birincisi güvenlik duvarı: durum takibi yapan bir güvenlik duvarı port 69’a giden isteğin cevabını aynı porttan bekler, başka porttan geleni düşürebilir. Bunu TFTP nedir yazısı için Windows Güvenlik Duvarı’nın arkasındaki bir istemcide ölçtüm: istemci isteği tekrarladıkça sunucu her seferinde yeni bir port açtı ve aktarım tamamlanmadı.

İkincisi teşhis. Sunucunun hata cevabını yakalamak için yalnız port 69’u dinlemek yetmiyor. udp port 69 filtresiyle aynı başarısız yazmayı dinlediğimde yalnız istek yakalandı, hata cevabı filtrenin dışında kaldı:

Çıktı
18:17:30.371277 IP 127.0.0.1.55104 > 127.0.0.1.69: TFTP, length 50, WRQ "olmayan.txt" octet tsize 2 blksize 512 timeout 6

Linux TFTP Sunucusu Sorun Giderme

  • Cihaz “File transfer failed” diyor, sunucunun günlüğü boş. --create eksik ya da /srv/tftp sahibi tftp değil. Sunucunun cevabını görmek için port 69 ile sınırlamadan dinleyin, ör. sudo tcpdump -i any -n -X udp and host <cihazın adresi>. 0005 0001 File not found cevabı bu sebebi doğrular.
  • Yükleme 0005 0002 File must have global write permissions ile reddediliyor. --umask verilmiş ama --permissive verilmemiş. Geride kalan 0 baytlık dosyayı silmeyi unutmayın.
  • systemctl servisi active gösteriyor ama hiçbir istek cevap almıyor. Lab’da bir yeniden başlatmada günlüğe Unit process 1929 (in.tftpd) remains running after unit stopped. düştü; sonrasında port 69’da dinleyen yoktu, systemctl is-active ise active diyordu. Sebebini doğrulayamadım; in.tftpd kendi hatasını günlüğe yazmadı. İşe yarayan yöntem şuydu: servisi durdurmak, pgrep -x in.tftpd boş dönene kadar beklemek, başlatmak ve sudo ss -ulnp | grep ':69 ' ile dinleyiciyi doğrulamak. Bu yöntemle dört yeniden başlatma sorunsuz geçti. Servisin çalıştığını systemctl ile değil, ss ile doğrulayın.
  • Aktarım başlıyor ama yarıda kalıyor. Araya giren bir güvenlik duvarı yeni porttan dönen cevabı düşürüyor olabilir.
  • Dosya dizinin dışında görünüyor. --secure açıkken istemcinin istediği her yol /srv/tftp içinde aranır; dosyanın o dizinde durduğundan emin olun.

TFTP mi, SFTP mi? Kurulumdan Sonraki Asıl Karar

TFTP sunucusunu kurmak kolaydır, çünkü TFTP kimlik doğrulaması ve şifreleme taşımaz. Bu kolaylık aynı zamanda sınırdır. Aynı switch yapılandırmasını hem TFTP hem SFTP ile taşıdığımda TFTP kopyasının içinde SNMP community string düz metin çıktı, SFTP kopyasında çıkmadı; iki yakalamayı TFTP nedir yazımızda yan yana koyduk. Lab switch’im TFTP’nin yanında SCP ve SFTP’yi de destekliyor. TFTP sunucusunu ayakta tutmanın gerekçesi, yalnız TFTP konuşan cihazlardır; onun dışında yedek şifreli yoldan taşınmalıdır.

TFTP’nin bir de ikinci işi var: ağdan işletim sistemi başlatan PXE, açılış dosyasını bir TFTP sunucusundan çeker. Fotokopi odasının aynı makinesi, bu kez bir kurulum imajını çoğaltır.

Sonuç: Kutudan Çıkan Hâlden Üç Bayrak Uzakta

Linux’ta TFTP sunucusu kurmak tek pakettir, ama kutudan çıkan hâli ilk yedeği almaz ve aldığı yedeği herkese açık bırakır. Lab’daki ölçümler üç bayrak gösterdi:

  1. --create, istemcinin yeni dosya yazabilmesini sağlar.
  2. --umask 077, yazılan yedeği kapatır.
  3. --permissive, umask’ın çalışmasını sağlar.

--secure kutudan zaten açık gelir ve öyle kalmalıdır. Geri kalan iş TFTP’nin kendisinde değil, sunucunun yerindedir: dinleme yönetim ağına sabitlenmeli ve mümkünse yedek trafiği SFTP’ye taşınmalıdır.

Fotokopi odasını kurmak kolaydır; çıktı tepsisini kimin eline bırakacağınıza karar vermek, işin asıl kısmıdır. Aynı Ubuntu sunucuda switch yedeğini TFTP yerine SSH ile, her gece ve yalnız değişince alan düzeni switch yapılandırma yedeği yazımızda kurduk.

Linux TFTP Sunucusu Kurulumu Hakkında Sık Sorulan Sorular

En sık sebep, sunucunun yeni dosya oluşturmaya yetkili olmamasıdır. tftpd-hpa --create bayrağı olmadan yalnız zaten var olan bir dosyanın üzerine yazar; cihaz yeni bir yedek gönderince sunucu "File not found" hatası döner. Lab'da bu hatanın sunucunun günlüğüne düşmediğini, yalnız ağ trafiğinde göründüğünü ölçtüm. Çözüm --create eklemek ve /srv/tftp dizinini tftp kullanıcısına devretmektir.
Varsayılan ayarla sunucudaki herkes. tftpd-hpa --create ile oluşturduğu dosyaları 666 izniyle, yani herkesin okuyup yazabileceği biçimde açar. --umask 077 bunu 600'e indirir ama tek başına yüklemeyi bozar: sunucu "File must have global write permissions" diyerek reddeder ve geride boş bir dosya bırakır. Lab'da ikisinin birlikte çalışması için --permissive bayrağının da gerektiğini ölçtüm.
TFTP kimlik sormaz ve dosyaları şifrelemeden taşır; sunucuyu ağda görebilen herkes ondan dosya okuyabilir. --secure bayrağı sunucuyu kendi dizinine hapseder ama bir parola getirmez. Bu yüzden TFTP sunucusu yalnız yönetim ağında durmalı, dinlediği adres o ağa sabitlenmeli ve yedek trafiği mümkünse şifreli bir protokole taşınmalıdır.
Cihazınız destekliyorsa evet. Aynı switch yapılandırmasını iki protokolle taşıdığımda TFTP kopyasının içinde SNMP community string düz metin çıktı, SFTP kopyasında çıkmadı. Lab switch'im TFTP'nin yanında SCP ve SFTP'yi de destekliyor; kendi cihazınızın neyi desteklediğini copy komutunun yardım çıktısından görebilirsiniz. TFTP'yi ayakta tutmanın gerekçesi, yalnız TFTP konuşan cihazlardır.
İlker Pehlivan
Yazan

BT Danışmanı | Ağ, Sistem ve Güvenlik Yönetimi

İlker Pehlivan, karmaşık BT altyapılarını ölçeklenebilir ve güvenli sistemlere dönüştürür. Buradaki rehberleri kendi laboratuvarında kurup ölçerek yazar.

LinkedIn

Benzer Makaleler

BT Sistem Yönetimi altındaki diğer 32 makale