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.
Installation und Konfiguration
manuelle Weg
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
automatisierter Weg mit Hilfe von Ansible
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
Systemtest
System-Test
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