TFTP-Server unter CentOS 8.x einrichten

Ein wesentlicher Baustein für unseren Installationsserver ist der TFTP-Server, der die Auslieferung der nötigen Dateien für den PXE-Bootvorgang bereitstellt und ausliefert. Eine genaue Beschreibung zu TFTP findet man auf der gut beschriebenen Wikipedia-Seite.

TFTP-Server installieren

Als erstes installieren wir uns die für den TFTP-Server notwendigen Pakete via dnf.

 #  dnf install syslinux tftp-server -y

Bei Bedarf informieren wir was bei der Installation der einzelnen Pakete im System installiert wurde.

 # rpm -qil syslinux
 # rpm -qil tftp-server

Firewall-Daemon anpassen

Damit die Clients später Verbindungsanfragen zu dem geöffneten Port tftp/69unseres TFTP-Servers stellen können, müssen wir für diese noch Änderungen am Paketfilter firewalld vornehmen.

Unter CentOS 8 wird als Standard-Firewall, wie auch schon bereits unter CentOS 7 eingeführt wurde, die dynamische firewalld verwendet. Ein großer Vorteil der dynamischen Paketfilterregeln ist unter anderem, dass zur Aktivierung der neuen Firewall-Regel(n) nicht der Daemon durchgestartet werden muss und somit alle aktiven Verbiundungen kurz getrennt werden. Sondern unsere Änderungen können on-the-fly aktiviert oder auch wieder deaktiviert werden.

Mit Hilfe des Programms firewall-cmd legen wir nun eine permanente Regel in der Zone public für den Service *TFTP für Port 69 an.

 # firewall-cmd --permanent --zone=public --add-service=tftp
success

Anschliessend können wir den Firewall-Daemon einmal durchstarten und überprüfen, ob die Regeln auch entsprechend unserer Definition, gezogen haben.

 # firewall-cmd --reload
success

Zu guter letzt fragen wir ab, welche Dienste in der Zone public geöffnet sind.

 # firewall-cmd --zone=public --list-services
cockpit dhcpv6-client http ssh tftp

tftp.service starten

Nun ist es an der Zeit unseren TFTP Server das erste mal mit Hilfe von systemd zu starten.

 # systemctl start tftp.service

Im Syslog wird uns der Start des Daemon entsprechend protokolliert.

Jul  4 16:38:32 vml000190 systemd[1]: Listening on Tftp Server Activation Socket.
Jul  4 16:38:32 vml000190 systemd[1]: Started Tftp Server.

Ob auf unserem Server nun der TFTP Server läuft und auf Port 69 horcht können wir bei Bedarf auch noch überprüfen.

 # netstat -tulpen | grep 69

udp6       0      0 :::69                   :::*                                0          78244      1/systemd

Ebenso kann man den Status des Daemons mit Hilfe des Befehls systemctl abfragen.

 # systemctl status tftp.service

 tftp.service - Tftp Server
   Loaded: loaded (/usr/lib/systemd/system/tftp.service; indirect; vendor preset: disabled)
   Active:active (running) since Sat 2020-07-04 16:36:44 CEST; 1min 7s ago
     Docs: man:in.tftpd
 Main PID: 3920 (in.tftpd)
    Tasks: 1 (limit: 12494)
   Memory: 176.0K
   CGroup: /system.slice/tftp.service
           └─3920 /usr/sbin/in.tftpd -s /var/lib/tftpboot

Jul 04 16:36:44 vml000190.dmz.nausch.org systemd[1]: Started Tftp Server.

Den automatischen Start unseres TFTP-Serverdienstes aktivieren wir für den Systemstart nun noch mit:

 # systemctl enabled tftp.service
Created symlink /etc/systemd/system/sockets.target.wants/tftp.socket → /usr/lib/systemd/system/tftp.socket.

Ob der Dxinetd-Service gestartet wird können wir bei Bedarf wie folgt abfragen:

 # systemctl is-enabled tftp.service tftp.socket
indirect
enabled

Da wir aber in unserer Produktiven Umgebung alle Server, Dienste und Services mit Hilfe von Ansible werden wir natürlich auch für unseren TFTP-Dienst dies über eine entsprechende Ansible-Rolle ansible-role-tftp.tar.gz1) erledigen lassen.

Wir holen uns also das Verzeichnis mit der role auf unseren Rechner.

 $ wget https://dokuwiki.nausch.org/lib/exe/fetch.php/centos:pxe_c8:ansible-role-tftp.tar.gz -O ansible-role-tftp.tar.gz

Da wir bei der Installation und Konfiguration von Ansible bereits das Ansible Directory Layout erzeugt hatten, brauchen wir nur noch das heruntergeladene Archiv an Ort und Stelle entpacken.

 $ tar -xvf ansible-role-tftp.tar.gz -C ~/ansible/roles/

Jetzt brauchen wir nur noch die Rolle unserem Server tftp-server zuordnen.

 $ vim ~/ansible/tftp.yml
~/ansible/tftp.yml
--- # Start des Playbooks für den TFTP-Server
- hosts: tftp-server
 
  roles:
    #- base   # Basiskonfiguration (User anlegen)
    #- chrony # Installation und Konfiguration NTP-Client
    - tftp   # Installation und Konfiguration TFTP-Server
 
... # Start des Playbooks für den TFTP-Server

Nun brauchen wir nur noch das Playbook ausführen und unser TFTP-Server steht bereit.

 $ ansible-playbook -v ~/ansible/tftp.yml

Using /home/ansible/.ansible.cfg as config file
BECOME password: 

PLAY [tftp-server] *************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [tftp-server]
TASK [tftp : ***TFTP*** : Installation der TFTP-Server relevanten Pakete (in der aktuellsten Version)] ****************************** changed: [tftp-server] => {"changed": true, "msg": "", "rc": 0, "results": ["Installed: xinetd-2:2.3.15-24.el8.x86_64", "Installed: mtools-4.0.18-14.el8.x86_64", "Installed: tftp-server-5.2-24.el8.x86_64", "Installed: syslinux-6.04-4.el8.x86_64", "Installed: syslinux-nonlinux-6.04-4.el8.noarch"]}
TASK [tftp : ***TFTP*** : Firewall-Daemon für den TFTP-Server anpassen] ************************************************************* changed: [tftp-server] => {"changed": true, "msg": "Permanent operation, Changed service tftp to enabled"}
TASK [***TFTP*** : Sicherstellen dass der Daemon 'tftp.service' gestartet wird und läuft] ****************************************** changed: [tftp-server] => {"changed": true, "enabled": true, "name": "tftp.service", "state": "started", "status": {"ActiveEnterTimestampMonotonic": "0", "ActiveExitTimestampMonotonic": "0", "ActiveState": "inactive", "After": "sysinit.target basic.target tftp.socket system.slice", "AllowIsolate": "no", "AllowedCPUs": "", "AllowedMemoryNodes": "", "AmbientCapabilities": "", "AssertResult": "no", "AssertTimestampMonotonic": "0", "Before": "shutdown.target", "BlockIOAccounting": "no", "BlockIOWeight": "[not set]", "CPUAccounting": "no", "CPUAffinity": "", "CPUQuotaPerSecUSec": "infinity", "CPUSchedulingPolicy": "0", "CPUSchedulingPriority": "0", "CPUSchedulingResetOnFork": "no", "CPUShares": "[not set]", "CPUUsageNSec": "[not set]", "CPUWeight": "[not set]", "CacheDirectoryMode": "0755", "CanIsolate": "no", "CanReload": "no", "CanStart": "yes", "CanStop": "yes", "CapabilityBoundingSet": "cap_chown cap_dac_override cap_dac_read_search cap_fowner cap_fsetid cap_kill cap_setgid cap_setuid cap_setpcap cap_linux_immutable cap_net_bind_service cap_net_broadcast cap_net_admin cap_net_raw cap_ipc_lock cap_ipc_owner cap_sys_module cap_sys_rawio cap_sys_chroot cap_sys_ptrace cap_sys_pacct cap_sys_admin cap_sys_boot cap_sys_nice cap_sys_resource cap_sys_time cap_sys_tty_config cap_mknod cap_lease cap_audit_write cap_audit_control cap_setfcap cap_mac_override cap_mac_admin cap_syslog cap_wake_alarm cap_block_suspend", "CollectMode": "inactive", "ConditionResult": "no", "ConditionTimestampMonotonic": "0", "ConfigurationDirectoryMode": "0755", "Conflicts": "shutdown.target", "ControlPID": "0", "DefaultDependencies": "yes", "Delegate": "no", "Description": "Tftp Server", "DevicePolicy": "auto", "Documentation": "man:in.tftpd", "DynamicUser": "no", "EffectiveCPUs": "", "EffectiveMemoryNodes": "", "ExecMainCode": "0", "ExecMainExitTimestampMonotonic": "0", "ExecMainPID": "0", "ExecMainStartTimestampMonotonic": "0", "ExecMainStatus": "0", "ExecStart": "{ path=/usr/sbin/in.tftpd ; argv[]=/usr/sbin/in.tftpd -s /var/lib/tftpboot ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", "FailureAction": "none", "FileDescriptorStoreMax": "0", "FragmentPath": "/usr/lib/systemd/system/tftp.service", "GID": "[not set]", "GuessMainPID": "yes", "IOAccounting": "no", "IOSchedulingClass": "0", "IOSchedulingPriority": "0", "IOWeight": "[not set]", "IPAccounting": "no", "IPEgressBytes": "18446744073709551615", "IPEgressPackets": "18446744073709551615", "IPIngressBytes": "18446744073709551615", "IPIngressPackets": "18446744073709551615", "Id": "tftp.service", "IgnoreOnIsolate": "no", "IgnoreSIGPIPE": "yes", "InactiveEnterTimestampMonotonic": "0", "InactiveExitTimestampMonotonic": "0", "JobRunningTimeoutUSec": "infinity", "JobTimeoutAction": "none", "JobTimeoutUSec": "infinity", "KeyringMode": "private", "KillMode": "control-group", "KillSignal": "15", "LimitAS": "infinity", "LimitASSoft": "infinity", "LimitCORE": "infinity", "LimitCORESoft": "infinity", "LimitCPU": "infinity", "LimitCPUSoft": "infinity", "LimitDATA": "infinity", "LimitDATASoft": "infinity", "LimitFSIZE": "infinity", "LimitFSIZESoft": "infinity", "LimitLOCKS": "infinity", "LimitLOCKSSoft": "infinity", "LimitMEMLOCK": "65536", "LimitMEMLOCKSoft": "65536", "LimitMSGQUEUE": "819200", "LimitMSGQUEUESoft": "819200", "LimitNICE": "0", "LimitNICESoft": "0", "LimitNOFILE": "262144", "LimitNOFILESoft": "1024", "LimitNPROC": "7809", "LimitNPROCSoft": "7809", "LimitRSS": "infinity", "LimitRSSSoft": "infinity", "LimitRTPRIO": "0", "LimitRTPRIOSoft": "0", "LimitRTTIME": "infinity", "LimitRTTIMESoft": "infinity", "LimitSIGPENDING": "7809", "LimitSIGPENDINGSoft": "7809", "LimitSTACK": "infinity", "LimitSTACKSoft": "8388608", "LoadState": "loaded", "LockPersonality": "no", "LogLevelMax": "-1", "LogRateLimitBurst": "0", "LogRateLimitIntervalUSec": "0", "LogsDirectoryMode": "0755", "MainPID": "0", "MemoryAccounting": "yes", "MemoryCurrent": "[not set]", "MemoryDenyWriteExecute": "no", "MemoryHigh": "infinity", "MemoryLimit": "infinity", "MemoryLow": "0", "MemoryMax": "infinity", "MemorySwapMax": "infinity", "MountAPIVFS": "no", "MountFlags": "", "NFileDescriptorStore": "0", "NRestarts": "0", "NUMAMask": "", "NUMAPolicy": "n/a", "Names": "tftp.service", "NeedDaemonReload": "no", "Nice": "0", "NoNewPrivileges": "no", "NonBlocking": "no", "NotifyAccess": "none", "OOMScoreAdjust": "0", "OnFailureJobMode": "replace", "PermissionsStartOnly": "no", "Perpetual": "no", "PrivateDevices": "no", "PrivateMounts": "no", "PrivateNetwork": "no", "PrivateTmp": "no", "PrivateUsers": "no", "ProtectControlGroups": "no", "ProtectHome": "no", "ProtectKernelModules": "no", "ProtectKernelTunables": "no", "ProtectSystem": "no", "RefuseManualStart": "no", "RefuseManualStop": "no", "RemainAfterExit": "no", "RemoveIPC": "no", "Requires": "sysinit.target tftp.socket system.slice", "Restart": "no", "RestartUSec": "100ms", "RestrictNamespaces": "no", "RestrictRealtime": "no", "RestrictSUIDSGID": "no", "Result": "success", "RootDirectoryStartOnly": "no", "RuntimeDirectoryMode": "0755", "RuntimeDirectoryPreserve": "no", "RuntimeMaxUSec": "infinity", "SameProcessGroup": "no", "SecureBits": "0", "SendSIGHUP": "no", "SendSIGKILL": "yes", "Slice": "system.slice", "StandardError": "inherit", "StandardInput": "socket", "StandardInputData": "", "StandardOutput": "inherit", "StartLimitAction": "none", "StartLimitBurst": "5", "StartLimitIntervalUSec": "10s", "StartupBlockIOWeight": "[not set]", "StartupCPUShares": "[not set]", "StartupCPUWeight": "[not set]", "StartupIOWeight": "[not set]", "StateChangeTimestampMonotonic": "0", "StateDirectoryMode": "0755", "StatusErrno": "0", "StopWhenUnneeded": "no", "SubState": "dead", "SuccessAction": "none", "SyslogFacility": "3", "SyslogLevel": "6", "SyslogLevelPrefix": "yes", "SyslogPriority": "30", "SystemCallErrorNumber": "0", "TTYReset": "no", "TTYVHangup": "no", "TTYVTDisallocate": "no", "TasksAccounting": "yes", "TasksCurrent": "[not set]", "TasksMax": "12494", "TimeoutStartUSec": "1min 30s", "TimeoutStopUSec": "1min 30s", "TimerSlackNSec": "50000", "Transient": "no", "TriggeredBy": "tftp.socket", "Type": "simple", "UID": "[not set]", "UMask": "0022", "UnitFilePreset": "disabled", "UnitFileState": "indirect", "UtmpMode": "init", "WatchdogTimestampMonotonic": "0", "WatchdogUSec": "0"}}
PLAY RECAP ************************************************************************************************************************************* tftp-server : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Testdatei anlegen

Für unseren Test legen wir uns einfach eine kleine Textdatei im Arbeitsverzeichnis /var/lib/tftpboot/ an, die wir uns dann vom TFTP-Server herunterladen werden.

 # echo "Dies ist ein ganz einfacher Text für den Test unseres TFTP-Servers" > /var/lib/tftpboot/test

Testdatei transferieren

Anschließend melden wir uns auf einem Host im zugehörigen Netzwerksegment an, auf dem das RPM-Paket tftp (Client) installiert ist, an.

Dann bauen wir mit dem TFTP-Clientprogramm eine Verbindung zu unserem Server auf und holen uns die zuvor angelegte Datei test mit dem Befehl get. Nach dem Herunterladen verlassen wir das Programm mit dem Befehl quit.

Entweder über IP-Adresse des TFTP-Servers:

 # tftp -v 10.0.0.190

oder über den Hostnamen des TFTP-Servers

 # tftp -v tftp.dmz.nausch.org
 Connected to tftp.dmz.nausch.org (10.0.0.190), port 69
 tftp> get test
 getting from tftp.dmz.nausch.org:test to test [netascii]
 Received 69 bytes in 0.1 seconds [8714 bit/s]
 tftp> quit

Das Ganze geht natürlich auch schneller als „Einzeiler“:

 # tftp 10.0.0.190 -c get test

Fehlerbehandlung

Klappt der Download von unserem Clientrechners nicht, trotz funktionierendem Zugriff nicht, liegt es an den Paketfilterregelungen auf dem Clientrechner! Wie man in folgendem Beispiel sieht laufen wir hier in einen timeout:

 # tftp -v tftp.dmz.nausch.org -c get test
 Connected to tftp.dmz.nausch.org (10.0.0.190), port 69
 getting from tftp.dmz.nausch.org:test to test [netascii]
 Transfer timed out.

Was ist nun passiert? Auf dem Client gibt es doch keine iptables-Regel, die den Datentransfer beschneidet:

 # iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED 
ACCEPT     icmp --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:ssh 
REJECT     all  --  anywhere             anywhere            reject-with icmp-host-prohibited 

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
REJECT     all  --  anywhere             anywhere            reject-with icmp-host-prohibited 

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Auf unserem TFTP-Server haben wir doch auch Port 69 freigeschaltet:

 # firewall-cmd --zone=public --list-services
tftp dhcpv6-client http ssh

Im Syslog des TFTP-Servers sehen wir nur die Folgende Fehlermeldung:

 Jan 8 07:49:14 vml000057 in.tftpd[10031]: tftpd: read(ack): No route to host

Die Ursache ist ganz einfach erklärt. TFTP nutzt für die Steuerung Port 69, für die Datenübertragung werden dann aber beliebige highports genutzt. Damit diese nun unseren Paketfilter passieren können, müssen wir an dem betreffenden (Test-)Clientsystem, oder einem internen Paketfilter der unterschiedliche Sicherheitszonen trennt, die folgenden iptables-Module laden.

  • ip_conntrack_tftp
  • ip_nat_tftp

Auf einem CentOS 8 bzw. CentOS 7 System benutzen wir zur Aktivierung der beiden Module die Datei /etc/modules-load.d/iptables.conf.

 # vim /etc/modules-load.d/iptables.conf
/etc/modules-load.d/iptables.conf
# Django : 2016-01-08 - Zum Testen des TFTP-Servers nötige Änderungen
# default: none
ip_nat_tftp
ip_conntrack_tftp

Zum Aktivieren starten wir den betreffenden Host einmal durch.

 # systemctl reboot

Auf einem CentOS 6 System geschieht dies über die Konfigurationsdatei iptables-config des Paketfilter-Regelwerkes iptables. Dort tragen wir bei den nat helpers die beiden Module nach:

 # vim /etc/sysconfig/iptables-config
# Django 2011-11-24 Freischaltungen für TFTP-Transfers
# default : IPTABLES_MODULES=""
IPTABLES_MODULES="ip_conntrack_tftp ip_nat_tftp"

Anschließend starten wir unseren iptables-Paketfilter einmal durch.

 # service iptables restart
 iptables: Flushing firewall rules:                         [  OK  ]
 iptables: Setting chains to policy ACCEPT: filter          [  OK  ]
 iptables: Unloading modules:                               [  OK  ]
 iptables: Applying firewall rules:                         [  OK  ]
 iptables: Loading additional modules: ip_conntrack_tftp ip_[  OK  ]

Nun versuchen wir erneut unsere Testdatei vom TFTP-Server herunterzuladen , was natürlich Dank unserer Anpassungen nun erfolgreich funktioniert.

 $ tftp tftp.dmz.nausch.org -v -c get test
 Connected to tftp.dmz.nausch.org (10.0.0.57), port 69
 getting from tftp.dmz.nausch.org:test to test [netascii]
 Received 69 bytes in 0.1 seconds [6073 bit/s]

Im Syslog unseres TFTP-Servers wird der erfolgreiche Dateitransfer entsprechend protokolliert.

Jul  4 09:14:30 vml000190 xinetd[7708]: START: tftp pid=10145 from=10.0.0.27
Jul  4 09:14:30 vml000190 in.tftpd[10146]: RRQ from 10.0.0.107 filename test

Links


1)
Version v2 vom 05.07.2020
Diese Website verwendet Cookies. Durch die Nutzung der Website stimmen Sie dem Speichern von Cookies auf Ihrem Computer zu. Außerdem bestätigen Sie, dass Sie unsere Datenschutzbestimmungen gelesen und verstanden haben. Wenn Sie nicht einverstanden sind, verlassen Sie die Website.Weitere Information
  • centos/pxe_c8/tftp.txt
  • Zuletzt geändert: 05.07.2020 18:26.
  • von django