Wer gerne zwischen 2 Rechnern automatisch Dateien übertragen möchte, zum Beispiel für Backups oder auch einfach nur Bilder von der PiCam, der steht oftmals vor dem Problem nicht zu wissen wie er dies anstellen kann...
Ich möchte euch hier ein paar Möglichkeiten beschreiben wie ihr eine solche Übertragung automatisieren könnt, egal ob regelmäßig via crontab oder manuell angestoßen.
Dabei gibt es je nach Methode ein paar Dinge zu beachten, was inbesondere die Sicherheit betrifft aber auch Voraussetzungen die erfüllt sein müssen.
Auch kann man sich meistens aussuchen auf welchem Rechner das Script zur Übertragung eingerichtet wird. Es muss nämlich nicht da laufen wo die Datei zur Übertragung erstellt wird - Wer es Zeitlich abstimmt (erstellen und das herunterladen der Datei) kann das Script zur Übertragung auch auf dem Ziel-Rechner laufen lassen.
- Methode über FTP (File Transfer Protocol) oder auch FTPS (FTP over SSL)
- Setzt voraus das die Gegenstelle einen FTP-Server installiert hat und ihr auch einen FTP-Benutzer habt der im jeweiligen Verzeichnis schreiben darf
- Methode über SFTP (FTP over SSH)
- Setzt voraus das die Gegenstelle einen SSH-Server installiert hat und ihr auch einen SSH-Benutzer habt der im jeweiligen Verzeichnis schreiben darf
- Methode über SCP (Secure CoPy)
- Setzt voraus das die Gegenstelle einen SSH-Server installiert hat und ihr auch einen SSH-Benutzer habt der im jeweiligen Verzeichnis schreiben darf
Technischer Hintergrund:
Spoiler anzeigen
Als SSH (Secure Shell) werden Protokolle und gleichzeitig eine Suite aus Anwendungen bezeichnet, die SSH implementieren. OpenSSH wird vom OpenBSD Projekt gepflegt und weiterentwickelt. Die Protokolle werden in den Internet- Drafts der IETF Working Group Secure Shell (secsh) definiert.
Der Vorteil von SSH, SCP und SFTP im Gegensatz zu FTP oder Telnet liegt darin, dass beginnend bei der Authentifizierung von User und Server bis zum Transfer der Daten alles kryptographisch verschlüsselt wird. Dabei kommen die gleichen Algorithmen zum Einsatz wie zB bei PGP, u. a. AES, Triple-DES, Blowfish und CAST, DSA und RSA. Das heißt einem "Angreifer" ist es nicht möglich, Passwörter und Daten abzufangen, die bei FTP im Klartext übertragen werden.
SCP ist die Abkürzung für "Secure Copy", eine Anwendung, um direkt Dateien zwischen Server und User zu übertragen. SFTP ist die Abkürzung für "Secure File Transfer Program", einem interaktiven Dateitransferprogramm ähnlich FTP, mit dem der User vor dem eigentlichen Transfer Verzeichnisse und deren Inhalt auf dem Server einsehen und Kommandos auf dem Server ausführen kann.
Gerade in Zeiten, wo den Vertretern von Rechteinhabern, Strafverfolgungsbehörden und Geheimdiensten immer umfangreichere Überwachungsmöglichkeiten zugesprochen werden und sich der Kreis von Personen vergrößert, die mit den nötigen "Hackerskills" ausgestattet, ebenfalls Überwachungs- und Abfangaktionen durchführen, sollte sich parallel die Zahl der Serverbetreiber erhöhen, die SCP und SFTP statt FTP anbieten und die Zahl der User, die diese Angebote nutzen können.
Weitere Informationen:
Wer mithilfe von WinSCP automatisierte Übertragungen realisieren möchte der muss sich > hier < durchfuchsen
Mögliche Parameter / Argumente die die oben aufgeführten Befehle haben können - wie immer - über die manual Pages eingesehen werden, zum Beispiel über den Befehl: man sftp
1. Methode über FTP
Hier gibt es verschiedene Möglichkeiten wie man es umsetzen möchte.
- Eine Möglichkeit wäre die FTP Zugangsdaten direkt in das Script einzufügen.
- Eine andere Möglichkeit wäre die Zugangsdaten in die Datei .netrc zu hinterlegen die im Benutzerverzeichnis liegt und vom FTP-Client automatisch ausgelesen wird sofern die Datei existiert.
Bei beiden Möglichkeiten muss man beachten das die Zugangsdaten unverschlüsselt gespeichert werden. Ihr solltet also unbedingt darauf achten das zum einen nur Ihr den Account über den das Script läuft nutzt und zum anderen das ihr unterschiedliche Passwörter habt, also nicht für root das selbe pwd verwendet.
Voraussetzung hierfür ist auch ein installierter FTP-Client:
Bei den nachfolgenden Scripts gibt es den eingangs bereits erwähnten Unterschied auf welchem Rechner ihr das Script ausführt:
- Wenn ihr das Script auf dem selben Rechner wie die zu übertragende Datei ausführt lautet der ftp Befehl put
- Wenn ihr das Script aber auf dem Ziel-Rechner ausführt muss der ftp-Befehl get lauten!
Eine Neuerung in aktuellen Clients besteht aber auch darin mehrere Dateien auf einmal mithilfe des Commandos mput zu übertragen (multi put). Umgekehrt geht auch mget.
put --> upload
get --> download
Zur 1.Möglichkeit:
Solch ein Script könnte zum Beispiel wie folgt aussehen:
Spoiler anzeigen
#!/bin/bash
#
# Titel: FTP Transfer Script
# Description: Datei automatisch auf einen festgelegten FTP-Server hochladen.
# Version: 0.1
### EINSTELLUNGEN
FTP_SERVER=Adresse_des_FTP-Servers #Bsp.: 192.168.0.100
FTP_USER=Benutzername
FTP_PASS=Passwort
#Die Datei welche uebertragen werden soll
FILE2TRANSFER=/home/pi/Bild.jpg
#Das Verzeichnis wohin die Datei uebertragen werden soll
REMOTEDIR=/var/www/picam/
### ENDE DER EINSTELLUNGEN
# Dateien per FTP auf den Server schieben
ftp -ni << END_UPLOAD
open $FTP_SERVER
user $FTP_USER $FTP_PASS
cd $REMOTEDIR
bin
mput $FILE2TRANSFER $(basename $FILE2TRANSFER)
quit
END_UPLOAD
exit 0
Alles anzeigen
Erklärung zu den Befehlen unter # Dateien per FTP auf den Server schieben:
Die erste Zeile schaltet den interaktiven Modus von FTP aus ( -i ) und unterbindet die automatische FTP-Anmeldung beim Verbindungsaufbau ( -n ), ansonsten würde er zunächst nach einer .netrc Datei suchen und wenn dort kein passender Host-Eintrag vorhanden wär, würde er nach den Zugangsdaten Fragen..
Um ftp mit Befehlen zu füttern kommt anschließend ein so genanntes Here-Dokument zum Einsatz. Alle Kommandos zwischen der Startmarke <<END_UPLOAD und der gleichnamigen Schlussmarke END_UPLOAD übergibt die Shell dem FTP-Client als Eingabe. Statt END_UPLOAD kann aber auch ein beliebiger Namen für die Marke verwendet werden.
Zunächst baut die Befehlsfolge eine Verbindung zum Host $FTP_SERVER auf. Anschließend legt das Skript für die Übertragung mit bin den Binäry-Modus fest. Das ist wichtig für die jeweilige Datei richtig zu wählen, Textdateien sollten zum Beispiel im ASCII-Modus übertragen werden.
Das Script wechselt dann auf der Gegenseite nach $REMOTEDIR. Dann findet die eigentliche Übertragung der Datei $FILE2TRANSFER statt. Der Befehl quit beendet den FTP-Client anschließend.
Zur 2.Möglichkeit:
Hierbei ist wichtig das sich die Datei .netrc in dem Userverzeichnis (HOME) befindet über wen auch das Script ausgeführt wird. In das jeweilige HOME-Verzeichnis gelangt ihr wenn ihr in der Konsole entweder nur cd eingebt, oder über die Linux typische Tilde ( ~ cd ~
Wichtig ist auch das die Dateirechte der Datei passen sodass nur der jeweilige Benutzer die Datei lesen und beschreiben darf (chmod 600).
Die Datei kann mehrere Einträge für Remote-Sites enthalten.
Sobald man eine .netrc besitzt und einen FTP-Befehl für eine dort aufgeführte Maschine eingebt, greift FTP auf den Benutzernamen und das Passwort des Eintrags zurück.
Die Einträge in der .netrc müssen in keinem bestimmten Format vorliegen; die einzelnen Informationen können wahlweise durch Leerzeichen, Tabulatoren oder Zeilenumbrüche getrennt sein. Recht übersichtlich fällt aber die Variante unter Verwendung von Zeilenumbrüchen aus:
Für jeden RemoteHost enthält die Datei einen Eintrag. Als 'password' wird das für den Aufbau der Verbindung erforderliche Passwort angegeben. Ein 'PasswortFuerKonto' wird nur dann gebraucht, wenn der Remote-Host eine zusätzliche Authentifizierung verlangt – in der Regel genügen aber die ersten drei Einträge: machine, login, password
Nachfolgend eine .netrc mit drei Einträgen, wobei hier alle Daten für eine Gegenstelle in einer Zeile stehen. Beim ersten Eintrag handelt es sich um einen öffentlichen FTP-Server im Web, deshalb der Loginname anonymous. Obwohl es als höflich gilt, die eigene E-Mail-Adresse als Passwort für die anonyme Anmeldung anzugeben, ist das nicht zwingend erforderlich. Die beiden anderen Zeilen stehen für Hosts im internen Netzwerk.
machine ftp.emea.ibm.com login anonymous password my@email.de
machine raspberrypi login dxtans password lOopy
machine 192.168.0.200 login dxtans password mas123
Um eine Verbindung zum Host raspberrypi aufzubauen brauch man nun nur ftp raspberrypi einzugeben. Der FTP-Client sucht dann in .netrc nach dem Hostnamen raspberrypi. Anschließend setzt er die Login- und Passworteinträge für den Verbindungsaufbau am Remote-Server automatisch ein.
Ein entsprechendes Script könnte dann wie folgt aussehen:
Spoiler anzeigen
#!/bin/bash
#
# Titel: FTP Transfer Script mit .netrc
# Description: Datei automatisch unter Verwendung der ~/.netrc auf einen festgelegten FTP-Server hochladen.
# Version: 0.1
### EINSTELLUNGEN
FTP_SERVER=Hostname_des_FTP-Servers_aus_netrc #Bsp.: 192.168.0.200
#Die Datei welche uebertragen werden soll
FILE2TRANSFER=/home/pi/Bild.jpg
#Das Verzeichnis wohin die Datei uebertragen werden soll
REMOTEDIR=/var/www/picam/
### ENDE DER EINSTELLUNGEN
# Dateien per FTP auf den Server schieben
ftp -i << END_UPLOAD
open $FTP_SERVER
cd $REMOTEDIR
bin
mput $FILE2TRANSFER $(basename $FILE2TRANSFER)
quit
END_UPLOAD
exit 0
Alles anzeigen
Beide Möglichkeiten kann man auch verwenden, um eine Liste der zu übertragenden Dateien anzugeben. Allerdings muss man dann für jeden Transfer eine Verbindung auf- und wieder abbauen. Eine for-Schleife innerhalb des Here-Codeblocks kann man nicht verwenden, weil man an dieser Stelle mit der FTP-Verbindung zum Remote-Host arbeitet aber nicht mit der Bash-Shell.
Wie das funktioniert zeigt das folgende sehr einfach gehaltene Script:
Spoiler anzeigen
#!/bin/bash
filelist="Bild1.jpg Bild2.jpg Video.avi"
for file in ${filelist}; do
ftp -i -v <<END_UPLOAD
open 192.168.0.200
bin
lcd /home/pi
cd /var/www/picam
mput ${file}
quit
END_UPLOAD
done
Alles anzeigen
Mit dem Befehl lcd wechselt er das Lokale Verzeichnis, also dort wo das Script ausgeführt wird. (local change directory)
Es besteht auch die Möglichkeit eine Fehlerbehandlung für die Scripts aus der 1. und 2. Möglichkeit zu verwenden. Ohne jetzt aber noch mal beide Scripts zu posten führe ich das nur anhand eines simplen Beispiels auf:
Spoiler anzeigen
#!/bin/bash
logfile=/tmp/ftp.log
filelist="Bild1.jpg Bild2.jpg Video.avi"
rm -f $logfile
for file in ${filelist}; do
ftp -i -v >> ${logfile} 2>&1 <<END_UPLOAD
open 192.168.0.200
bin
lcd /home/pi
cd /var/www/picam
mput ${file}
quit
END_UPLOAD
done
if egrep "202|421|426|450|500|501|503|550|553|666|777|999" ${logfile} > /dev/null 2>&1; then
echo "Errors" | tee -a ${logfile}
exit 1
else
echo "OK" | tee -a ${logfile}
exit 0
fi
Alles anzeigen
Das kann auch gerne etwas ausführlicher ausfallen indem man zu den Fehlercodes auch die jeweilige Meldung mit aufnimmt:
#!/bin/bash
#
logfile=/tmp/ftp.log
errorlog=/tmp/ftp.error
filelist="Bild1.jpg Bild2.jpg Video.avi"
#
## Return Codes
#(bash v3 doesnt support declare -A)
ReturnCodes=(
'202::Befehl nicht implementiert.'
'421::Dienst nicht verfuegbar.'
'426::Transfer abgebrochen.'
'450::Datei nicht verfuegbar.'
'500::Syntaxfehler.'
'501::Syntaxfehler in Argumenten.'
'503::Benutzer nicht angemeldet.'
'550::Datei nicht verfuegbar.'
'553::Ungueltiger Dateiname.'
'666::Datei oder Verzeichnis existiert nicht.'
'777::Unbekannter Host.'
'999::Ungueltiger Befehl.'
)
function ReturnCode() {
RC=$1
[[ "$RC" =~ ':' ]] && RC=$(echo $RC | sed -e s/.*://)
[[ "$RC" =~ ' ' ]] && RC=$(echo $RC | awk {'print $1'})
for index in "${ReturnCodes[@]}" ; do
KEY="${index%%::*}"
VALUE="${index##*::}"
[[ "$KEY" == "$RC" ]] && echo "$VALUE" && break
done
}
rm -f ${logfile}
for file in ${filelist}; do
ftp -i -v >> ${logfile} 2>&1 <<END_UPLOAD
open 192.168.0.200
bin
lcd /home/pi
cd /var/www/picam
mput ${file}
quit
END_UPLOAD
done
rm -f ${errorlog}
IFS=$'\n'
for line in $(cat ${logfile}); do
ReturnCode "$line" >> ${errorlog}
done
if [ -f ${errorlog} ] && [ "$(stat --printf="%s" ${errorlog})" -gt "0" ]; then
echo -e "ERROR:\n$(cat ${errorlog})"
exit 1
else
echo OK
exit 0
fi
Alles anzeigen
Dem Programmierer-Spieltrieb sind auch hier keine Grenzen gesetzt
2. Methode über SFTP
Bei diese Methode kann man nicht wie bei FTP über .netrc o.ä. verwenden, das funktioniert leider nicht.
Hier gibt es allerdings einen großen Vorteil gegenüber dem normalen FTP und zwar das man wie für SSH "public key authentication" verwenden kann und die Übertragung auch komprimiert werden kann. Ersteres steigert die Sicherheit enorm da nirgends ein Password im Klartext gespeichert werden muss oder beim Anmelden die Zugangsdaten im Klartext übertragen werden. Auch die eigentliche Übertragung der Dateien ist verschlüsselt da es über das SSH Protokoll abgewickelt wird.
Eine Besonderheit bei dieser Methode ist auch das kein bash Script benutzt werden kann, da es kein Parameter zur Übergabe eines Passworts gibt!
Stattdessen muss ein sog. expect Script verwendet werden, was auf bestimmte Aktionen interagieren kann.
Voraussetzung hierfür ist ein installierter SFTP-Client:
(sftp ist Bestandteil des ssh clients)
Eine weitere Voraussetzung ist die Scriptsprache expect:
(Automates interactive applications)
Die Syntax eines expect Scripts ist anders als bei bash!
Deshalb muss man für jede Datei die man übertragen will einen seperaten expect Abschnitt ins Script aufnehmen.
Ein solches expect Script könnte wie folgt aussehen:
Spoiler anzeigen
#!/usr/bin/expect
set remotePATH "/var/www/picam"
set localPATH "/home/pi/"
set localFILE "Bild.jpg"
set remoteIP "192.168.0.200"
set remotePORT "22"
set remoteUSER "pi"
set remotePASS "password"
set timeout 600
set LogFile "/tmp/sftp.log"
set sftp_prompt "sftp>?"
send_user "Transfering Bild.jpg ..\n"
spawn sftp -oPort=$remotePORT $remoteUSER@$remoteIP
send "mput $localFILE\r";
expect {
password: {
send "$remotePASS\r"; exp_continue
}
incorrect {
send_user "invalid password or account\n"
exit
}
#Check for common errors, by no means all of them
-re "Couldn't|(.*)disconnect|(.*)stalled" {
puts $LogFile "Unable to transfer file"
exit 1
}
# timeout {
# send_user "connection to $remoteIP timed out\n"
# exit
# }
# eof {
# send_user "connection to host failed: $expect_out(buffer)"
# exit
# }
# busy { puts busy\n ; exp_continue }
#OK continue
"$sftp_prompt" {
send "mput $localFILE\r"
puts $LogFile "File transfer completed"
}
}
Alles anzeigen
Man kann das Script aber auch so gestalten das die benötigten Informationen für den Transfer, an das Script übergeben werden was > hier < beschrieben wird.
Es ist aber auch möglich ein bash Script zu nutzen innerhalb dessen man expect aufruft:
Spoiler anzeigen
#!/bin/bash
HOST=192.168.0.200
PORT=22
USER=pi
PASS=password
FILE=/home/pi/Bild.jpg
/usr/bin/expect<<END_UPLOAD
set timeout 600
spawn sftp -o Port=$PORT $USER@$HOST
expect "password:"
send "$PASS\r"; exp_continue
expect "sftp>"
send "mput $FILE\r"
expect "sftp>"
send "exit"
END_UPLOAD
Alles anzeigen
3. Methode über SCP
Ähnlich wie bei SFTP muss auch hier ein expect Script verwendet werden.
Ein solches expect Script könnte wie folgt aussehen:
Spoiler anzeigen
#!/usr/bin/expect
set remotePATH "/var/www/picam"
set localPATH "/home/pi/"
set localFILE "Bild.jpg"
set remoteIP "192.168.0.200"
set remotePORT "22"
set remoteUSER "pi"
set remotePASS "password"
set timeout 600
send_user "Transfering $localFILE ..\n"
spawn scp -p -P $remotePORT $remoteUSER@$remoteIP:$remotePATH/$localFILE $localPATH
expect {
password: {
send "$remotePASS\r"; exp_continue
}
incorrect {
send_user "invalid password or account\n"
exit
}
# timeout {
# send_user "connection to $remoteIP timed out\n"
# exit
# }
# eof {
# send_user "connection to host failed: $expect_out(buffer)"
# exit
# }
# busy { puts busy\n ; exp_continue }
}
Alles anzeigen