Backup DA --> NAS (over FTP)

Alles over programmeren en development binnen de IT-wereld
Plaats reactie
2Flip
Premium Member
Premium Member
Berichten: 605
Lid geworden op: 12 mei 2010, 21:45
Uitgedeelde bedankjes: 45 keer
Bedankt: 33 keer

Zoals de titel al zegt: Ik probeer via Direct Admin een wekelijkse backup te maken (dit lukt), en ik zou hem nu graag automatisch naar mijn NAS thuis willen transfereren.

Ik heb hiervoor een scriptje gebruikt dat directadmin zelf op hun website heeft staan, maar het transfereren lukt me niet.
Ik heb via een CRON-job ervoor gezorgd dat de backup wekelijks wordt gemaakt (en dit werkt).
En nu zou dit bestand dus (via FTP) getransfereerd moeten worden naar mijn home NAS (via een 2de CRON-job)

FTP is reeds geactiveerd op de NAS (dus te bereiken op ftp://IP)
Echter begrijp ik de CRON-job niet goed:
1) "/usr/bin/ncftpput -f /home/username/ftp.conf -V -t 25 -m "/ftp/path//" "/home/username/backups/backup-`date +%b-%e-%Y`-1.tar.gz""

==> username veranderen door mijn gebruikersnaam, waar war moet ik veranderen aan "/ftp/path//"
Is dit het path van mijn NAS, zo ja, is dit dan ftp://IP/MAP?

2)
Met fto.conf:
host 1.2.3.4
user ftpuser
pass ftppass

==> moeten hier de gegevens komen van DirectAdmin, of van mijn NAS


Alvast bedankt!


PS
Voor zij die geïnteresseerd zijn; het volledige script:

Code: Selecteer alles

#!/usr/local/bin/php -n
<?php

//User backup script, allowing DA Users to create backups via a cronjob.

/*******************************
**  Set your variables here
*******************************/

$username = "youruserame";
$password = "yourpassword";

// you must specify one of your domain names. (Any one of them will be fine)
$domain = "yourdomain.com";

// These usually dont require modification
// if you use https to connect to DA, set $ssl = 1;
$host = "localhost";
$ssl = 0;
$port = 2222;

/*******************************
**  Install Instructions
*******************************

1) Place this file somewhere under your home directory.
A good place might be:
/home/username/backup.php

From the File Manager in DA, that would be:
/backup.php

2) Chmod the file to 700.  This is very important for security.

3) Edit the above variables.
Most likely, you''ll only need to change the username and password.
If your system uses ssl to connect to DA (https) then you'll need to set $ssl = 1;

4) Create the cronjob to execute:

/home/username/backup.php

as often as you wish. Usually once per day is the most you'll want to run it.
To create the cronjob, see:
http://www.site-helper.com/misc.html#cron

Usually a very early morning hour, say 5am is best as the load is usually lowest then.

Note that we do not need to add /usr/local/bin/php in front of this php script because of the
#!/usr/local/bin/php -n
line at the very top.

5) check your 'username' account.  Any emails will be delivered there if it doesn't work.
If needed, you can test manually via ssh by running:

/home/username/bacukp.php



6) Optional:  If you wish to have an ftp upload along with the bacukp, create a
cronjob to run about an hour later than the backup, and use the command:

/usr/bin/ncftpput -f /home/username/ftp.conf -V -t 25 -m "/ftp/path/" "/home/username/backups/backup-`date +%b-%e-%Y`-1.tar.gz"

and in the ftp.conf, put:

host 1.2.3.4
user ftpuser
pass ftppass

Then chmod the ftp.conf to 700.
Note this assumes you've only got 1 bacukp per day since it uses -1.tar.gz and doesn't take into account possible -2.tar.gz, etc.

********************************
*******************************/

$sock = newSock();
$c=0;
$sock->query('/CMD_API_SITE_BACKUP',
	array(
		'action' => 'backup',
		'domain' => $domain,
		'select'.$c++ => 'domain',
		'select'.$c++ => 'subdomain',
		'select'.$c++ => 'email',
		'select'.$c++ => 'forwarder',
		'select'.$c++ => 'autoresponder',
		'select'.$c++ => 'vacation',
		'select'.$c++ => 'list',
		'select'.$c++ => 'emailsettings',
		'select'.$c++ => 'ftp',
		'select'.$c++ => 'ftpsettings',
		'select'.$c++ => 'database',
	));

$result = $sock->fetch_parsed_body();

if ( $result['error'] != "0" )
{
	echo "Error issuing backup creation: ".$result['details']."\n";
	exit(1);
}

//all should be fine.
exit(0);

function newSock()
{
        global $host;
        global $port;
        global $ssl;
        global $username;
	global $password;

        $tsock = new HTTPSocket;
        $tsock->set_method('POST');
        $tsock->set_login($username, $password);

        if ($ssl)
        {
                $tsock->connect("ssl://$host", $port);
        }
        else
        {
                $tsock->connect("$host", $port);
        }

        return $tsock;
}





















/**
 * Socket communication class.
 *
 * Originally designed for use with DirectAdmin's API, this class will fill any HTTP socket need.
 *
 * Very, very basic usage:
 *   $Socket = new HTTPSocket;
 *   echo $Socket->get('http://user:[email protected]/somedir/some.file?query=string&this=that');
 *
 * @author Phi1 'l0rdphi1' Stier <[email protected]>
 * @package HTTPSocket
 * @version 2.6
 */
class HTTPSocket {

	var $version = '2.6';
	
	/* all vars are private except $error, $query_cache, and $doFollowLocationHeader */

	var $method = 'GET';

	var $remote_host;
	var $remote_port;
	var $remote_uname;
	var $remote_passwd;

	var $result;
	var $result_header;
	var $result_body;
	var $result_status_code;

	var $lastTransferSpeed;

	var $bind_host;

	var $error = array();
	var $warn = array();
	var $query_cache = array();

	var $doFollowLocationHeader = TRUE;
	var $redirectURL;

	var $extra_headers = array();

	/**
	 * Create server "connection".
	 *
	 */
	function connect($host, $port = '' )
	{
		if (!is_numeric($port))
		{
			$port = 80;
		}

		$this->remote_host = $host;
		$this->remote_port = $port;
	}

	function bind( $ip = '' )
	{
		if ( $ip == '' )
		{
			$ip = $_SERVER['SERVER_ADDR'];
		}

		$this->bind_host = $ip;
	}

	/**
	 * Change the method being used to communicate.
	 *
	 * @param string|null request method. supports GET, POST, and HEAD. default is GET
	 */
	function set_method( $method = 'GET' )
	{
		$this->method = strtoupper($method);
	}

	/**
	 * Specify a username and password.
	 *
	 * @param string|null username. defualt is null
	 * @param string|null password. defualt is null
	 */
	function set_login( $uname = '', $passwd = '' )
	{
		if ( strlen($uname) > 0 )
		{
			$this->remote_uname = $uname;
		}

		if ( strlen($passwd) > 0 )
		{
			$this->remote_passwd = $passwd;
		}

	}

	/**
	 * Query the server
	 *
	 * @param string containing properly formatted server API. See DA API docs and examples. Http:// URLs O.K. too.
	 * @param string|array query to pass to url
	 * @param int if connection KB/s drops below value here, will drop connection
	 */
	function query( $request, $content = '', $doSpeedCheck = 0 )
	{
		$this->error = $this->warn = array();
		$this->result_status_code = NULL;

		// is our request a http:// ... ?
		if (preg_match('!^http://!i',$request))
		{
			$location = parse_url($request);
			$this->connect($location['host'],$location['port']);
			$this->set_login($location['user'],$location['pass']);
			
			$request = $location['path'];
			$content = $location['query'];

			if ( strlen($request) < 1 )
			{
				$request = '/';
			}

		}

		$array_headers = array(
			'User-Agent' => "HTTPSocket/$this->version",
			'Host' => ( $this->remote_port == 80 ? $this->remote_host : "$this->remote_host:$this->remote_port" ),
			'Accept' => '*/*',
			'Connection' => 'Close' );

		foreach ( $this->extra_headers as $key => $value )
		{
			$array_headers[$key] = $value;
		}

		$this->result = $this->result_header = $this->result_body = '';

		// was content sent as an array? if so, turn it into a string
		if (is_array($content))
		{
			$pairs = array();

			foreach ( $content as $key => $value )
			{
				$pairs[] = "$key=".urlencode($value);
			}

			$content = join('&',$pairs);
			unset($pairs);
		}

		$OK = TRUE;

		// instance connection
		if ($this->bind_host)
		{
			$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
			socket_bind($socket,$this->bind_host);

			if (!@socket_connect($socket,$this->remote_host,$this->remote_port))
			{
				$OK = FALSE;
			}

		}
		else
		{
			$socket = @fsockopen( $this->remote_host, $this->remote_port, $sock_errno, $sock_errstr, 10 );
		}

		if ( !$socket || !$OK )
		{
			$this->error[] = "Can't create socket connection to $this->remote_host:$this->remote_port.";
			return 0;
		}

		// if we have a username and password, add the header
		if ( isset($this->remote_uname) && isset($this->remote_passwd) )
		{
			$array_headers['Authorization'] = 'Basic '.base64_encode("$this->remote_uname:$this->remote_passwd");
		}

		// for DA skins: if $this->remote_passwd is NULL, try to use the login key system
		if ( isset($this->remote_uname) && $this->remote_passwd == NULL )
		{
			$array_headers['Cookie'] = "session={$_SERVER['SESSION_ID']}; key={$_SERVER['SESSION_KEY']}";
		}

		// if method is POST, add content length & type headers
		if ( $this->method == 'POST' )
		{
			$array_headers['Content-type'] = 'application/x-www-form-urlencoded';
			$array_headers['Content-length'] = strlen($content);
		}
		// else method is GET or HEAD. we don't support anything else right now.
		else
		{
			if ($content)
			{
				$request .= "?$content";
			}
		}

		// prepare query
		$query = "$this->method $request HTTP/1.0\r\n";
		foreach ( $array_headers as $key => $value )
		{
			$query .= "$key: $value\r\n";
		}
		$query .= "\r\n";

		// if POST we need to append our content
		if ( $this->method == 'POST' && $content )
		{
			$query .= "$content\r\n\r\n";
		}

		// query connection
		if ($this->bind_host)
		{
			socket_write($socket,$query);

			// now load results
			while ( $out = socket_read($socket,2048) )
			{
				$this->result .= $out;
			}
		}
		else
		{
			fwrite( $socket, $query, strlen($query) );

			// now load results
			$this->lastTransferSpeed = 0;
			$status = socket_get_status($socket);
			$startTime = time();
			$length = 0;
			$prevSecond = 0;

			$readsize=1024;

			while ( !feof($socket) && !$status['timed_out'] )
			{
				$chunk = fgets($socket, $readsize);
				$length += strlen($chunk);
				$this->result .= $chunk;

				$elapsedTime = time() - $startTime;

				if ( $elapsedTime > 0 )
				{
					$this->lastTransferSpeed = ($length/$readsize)/$elapsedTime;
				}

				if ( $doSpeedCheck > 0 && $elapsedTime > 5 && $this->lastTransferSpeed < $doSpeedCheck )
				{
					$this->warn[] = "kB/s for last 5 seconds is below 50 kB/s (~".( ($length/$readsize)/$elapsedTime )."), dropping connection...";
					$this->result_status_code = 503;
					break;
				}

			}
			
			if ( $this->lastTransferSpeed == 0 )
			{
				$this->lastTransferSpeed = $length/$readsize;
			}

		}
		
		list($this->result_header,$this->result_body) = split("\r\n\r\n",$this->result,2);

		if ($this->bind_host)
		{
			socket_close($socket);
		}
		else
		{
			fclose($socket);
		}

		$this->query_cache[] = $query;


		$headers = $this->fetch_header();

		// what return status did we get?
		if (!$this->result_status_code)
		{
			preg_match("#HTTP/1\.. (\d+)#",$headers[0],$matches);
			$this->result_status_code = $matches[1];
		}

		// did we get the full file?
		if ( !empty($headers['content-length']) && $headers['content-length'] != strlen($this->result_body) )
		{
			$this->result_status_code = 206;
		}

		// now, if we're being passed a location header, should we follow it?
		if ($this->doFollowLocationHeader)
		{
			if ($headers['location'])
			{
				$this->redirectURL = $headers['location'];
				$this->query($headers['location']);
			}
		}
		
	}

	function getTransferSpeed()
	{
		return $this->lastTransferSpeed;
	}

	/**
	 * The quick way to get a URL's content :)
	 *
	 * @param string URL
	 * @param boolean return as array? (like PHP's file() command)
	 * @return string result body
	 */
	function get($location, $asArray = FALSE )
	{
		$this->query($location);

		if ( $this->get_status_code() == 200 )
		{
			if ($asArray)
			{
				return split("\n",$this->fetch_body());
			}

			return $this->fetch_body();
		}

		return FALSE;
	}

	/**
	 * Returns the last status code.
	 * 200 = OK;
	 * 403 = FORBIDDEN;
	 * etc.
	 *
	 * @return int status code
	 */
	function get_status_code()
	{
		return $this->result_status_code;
	}

	/**
	 * Adds a header, sent with the next query.
	 *
	 * @param string header name
	 * @param string header value
	 */
	function add_header($key,$value)
	{
		$this->extra_headers[$key] = $value;
	}

	/**
	 * Clears any extra headers.
	 *
	 */
	function clear_headers()
	{
		$this->extra_headers = array();
	}

	/**
	 * Return the result of a query.
	 *
	 * @return string result
	 */
	function fetch_result()
	{
		return $this->result;
	}

	/**
	 * Return the header of result (stuff before body).
	 *
	 * @param string (optional) header to return
	 * @return array result header
	 */
	function fetch_header( $header = '' )
	{
		$array_headers = split("\r\n",$this->result_header);
		
		$array_return = array( 0 => $array_headers[0] );
		unset($array_headers[0]);

		foreach ( $array_headers as $pair )
		{
			list($key,$value) = split(": ",$pair,2);
			$array_return[strtolower($key)] = $value;
		}

		if ( $header != '' )
		{
			return $array_return[strtolower($header)];
		}

		return $array_return;
	}

	/**
	 * Return the body of result (stuff after header).
	 *
	 * @return string result body
	 */
	function fetch_body()
	{
		return $this->result_body;
	}

	/**
	 * Return parsed body in array format.
	 *
	 * @return array result parsed
	 */
	function fetch_parsed_body()
	{
		parse_str($this->result_body,$x);
		return $x;
	}

}

?>
Afbeelding
DaNi0
Premium Member
Premium Member
Berichten: 749
Lid geworden op: 21 jun 2005, 13:45
Locatie: Oostende
Uitgedeelde bedankjes: 482 keer
Bedankt: 28 keer
Te Koop forum

Zie ook: ncftpput manpage

Dat is inderdaad het pad op de FTP server. Het pad moet niet het ftp://<IP> prefix krijgen, want je hebt de remote host reeds gespecificeerd in je ftp.conf configuratiebestand. Het gaat dus om hetzelfde soort pad als je lokale pad, bv. /home/username/serverbackups/.

Je kan trouwens prima het commando manueel uitvoeren om te testen of de syntax correct is en er zich geen fouten voordoen.
2Flip
Premium Member
Premium Member
Berichten: 605
Lid geworden op: 12 mei 2010, 21:45
Uitgedeelde bedankjes: 45 keer
Bedankt: 33 keer

Bedankt, path is nu juist ingevoerd.
Commando manueel uitvoeren gaat helaas niet (heb geen admin rechten ;) )

Heb de cron laten lopen, hopelijk doet ie het nu wel.
Afbeelding
2Flip
Premium Member
Premium Member
Berichten: 605
Lid geworden op: 12 mei 2010, 21:45
Uitgedeelde bedankjes: 45 keer
Bedankt: 33 keer

Tot nu toe ben ik er nog steeds niet in geslaagd dit werkende te krijgen, maar ik denk dat ik weet wat er fout loopt (ja, mijn wangen lopen al rood aan).

Ik heb namelijk steeds het IP-adres opgegeven (van de NAS) dat ik terugvond in "mijn telenet".
Ik had er niet bij stilgestaan, maar ik ga er van uit dat dit een lokaal IP-adres is (192.168.0.xxx).

Op het gebied van netwerken ben ik nooit echt sterk geweest, dus kan iemand me uitleggen hoe ik er voor kan zorgen dat ik mijn NAS ook van buitenuit te bereiken is.

Alvast bedankt!
Afbeelding
DaNi0
Premium Member
Premium Member
Berichten: 749
Lid geworden op: 21 jun 2005, 13:45
Locatie: Oostende
Uitgedeelde bedankjes: 482 keer
Bedankt: 28 keer
Te Koop forum

Dat is inderdaad je intern IP adres. Eerst en vooral zul je moeten je extern IP adres te weten komen. Dit kun je makkelijk te weten te komen door naar http://www.whatismyip.org/ te surfen. Telenet gebruikt echter dynamische IP adressen, dit wil zeggen dat je IP adres soms zal veranderen. Hoe vaak dit gebeurt bij Telenet ben ik niet zeker, maar vroeger duurde dit toch enkele maanden voor sommigen. Dit euvel kan ook opgelost worden door gebruik te maken van de diensten van DynDNS of gelijkaardige aanbieders. Zij bieden je een subdomein aan, bijvoorbeeld 2flip.dyndns.com die dan automatisch omgezet wordt naar je IP adres. Zodanig hoef je je IP adres niet meer te onthouden. Je draait dan een programmatje die automatisch deze verwijzing update als je IP adres verandert. Dit is ook geïntegreerd in sommige thuisrouters zodat je niks hoeft te installeren. Maar ik vrees dat dit waarschijnlijk het geval niet zal zijn bij de modem van Telenet. Je kunt eens rondsnuisteren in je modem beheer in je Telenet portaal of je iets terugvindt. Om te testen hoef je echter nog geen gebruik te maken van DynDNS.

Nadat je het extern IP adres hebt, moet je er ook voor zorgen dat je NAS toegankelijk is voor het internet. Dit kun je doen door de juiste poorten door te verwijzen (portforwarding) naar het intern IP adres van je NAS. FTP is standaard poort 21, dus dan moet je die openzetten. Dit zul je opnieuw moeten nazien in het beheer van je modem (Mijn Telenet). Voor PASV modus zul je ook poort 20 moeten openzetten. Dit wordt gebruikt voor als de FTP client zich achter een firewall bevindt.

Als dit alles is gelukt zou je normaal gezien moeten kunnen verbinden met je FTP server met het extern IP adres dat je eerder hebt gevonden.
TomG
Elite Poster
Elite Poster
Berichten: 2173
Lid geworden op: 06 jun 2005, 18:33
Locatie: Zwevegem
Uitgedeelde bedankjes: 476 keer
Bedankt: 106 keer

Alle inkomende poorten <1024 staan dicht op het Telenet netwerk. Dus dat zal nooit werken op die manier.

Kan je geen rsync over ssh doen naar je NAS? Da's maar één poort (22) die je makkelijk kan veranderen (bvb naar 2022).
2Flip
Premium Member
Premium Member
Berichten: 605
Lid geworden op: 12 mei 2010, 21:45
Uitgedeelde bedankjes: 45 keer
Bedankt: 33 keer

DaNi0 schreef:Dat is inderdaad je intern IP adres. Eerst en vooral zul je moeten je extern IP adres te weten komen. Dit kun je makkelijk te weten te komen door naar http://www.whatismyip.org/ te surfen.
Ik weet dat ik via deze manier het IP-adres van mijn pc kan achterhalen, maar hoe doe ik dit voor de NAS?
Ik heb trouwens mijn fonera 2.0n in Bridge-mode staan, en hier DynDNS kan hierop geactiveerd worden. Ik twijfel er echter aan of dit kan icm de telenet modem.

DaNi0 schreef: Nadat je het extern IP adres hebt, moet je er ook voor zorgen dat je NAS toegankelijk is voor het internet. Dit kun je doen door de juiste poorten door te verwijzen (portforwarding) naar het intern IP adres van je NAS. FTP is standaard poort 21, dus dan moet je die openzetten. Dit zul je opnieuw moeten nazien in het beheer van je modem (Mijn Telenet). Voor PASV modus zul je ook poort 20 moeten openzetten. Dit wordt gebruikt voor als de FTP client zich achter een firewall bevindt.
Poorten <1024 zijn inderdaad gesloten bij telenet, maar ik ga er van uit dan ik dan wel een poort <1024 kan kiezen...
TomG schreef:Kan je geen rsync over ssh doen naar je NAS? Da's maar één poort (22) die je makkelijk kan veranderen (bvb naar 2022).
Met Rsync heb ik geen ervaring. Gezien ik wekelijks een backup wil maken leek het me het makkelijkst om de backup gewoon te versturen naar de NAS. Maar blijkbaar is het zo simpel niet ;)
Afbeelding
DaNi0
Premium Member
Premium Member
Berichten: 749
Lid geworden op: 21 jun 2005, 13:45
Locatie: Oostende
Uitgedeelde bedankjes: 482 keer
Bedankt: 28 keer
Te Koop forum

Dan ga je moeten in de FTP server configuratie de poort moeten veranderen naar iets boven de 1024, oftewel een hoge poort mappen naar 21 intern.

Je maakt gebruik van interne IP adressen, dus dit wil zeggen dat er Netwerk Address Translation (NAT) plaatsvindt, het is te zeggen, interne IP adressen worden vertaald naar externe en omgekeerd. Wat er bij jou gebeurt is dat al je interne IP adressen vertaald worden naar een extern IP adres, dus het IP adres dat je te zien krijgt met je PC is die die je ook zal gebruiken voor je NAS. Het onderscheid wordt gemaakt door middel van de portmapping (waar je het intern IP van je NAS opgeeft).

DynDNS zal normaal wel lukken met je fonera.
2Flip
Premium Member
Premium Member
Berichten: 605
Lid geworden op: 12 mei 2010, 21:45
Uitgedeelde bedankjes: 45 keer
Bedankt: 33 keer

Ahaa, het één en ander wordt me nu duidelijk. Bedankt, ik zal en één van de volgende dagen mee aan de slag gaan!

UPDATE:
De cron werkt ondertussen, blijkbaar moesten de % nog "ge-espaced" worden (door een "simpele" \ er voor te plaatsen)
Dus:
/usr/bin/ncftpput -f /home/username/ftp.conf -V -t 25 -m "/Mapftp/Map2/" "/home/username/backups/backup-`date +\%b-\%e-\%Y`-1.tar.gz"


Nu loop ik nog tegen het volgende probleem aan:
FTP is niet bereikbaar van buitenaf.

Poort 2221-2222 geforward in "mijn telenet" naar het intern IP adres van de NAS.
Ik veronderstel dat ik nu nog enkele poorten moet aanpassen in de config van mijn fonera 2.0n die in Bridge-mode staat; maar ik kom er niet echt uit.

1Protocol 2Bron Poort 3IP Adres 4Doel Poort --> dit moet ik ingeven

1) TCP (of + UDP?)
2) 20/21 OF 2222/2221 ?
3) intern IP (192.xx), of extern IP (84.xx)?
4) 20/21 OF 2222/2221 ?
Afbeelding
Plaats reactie

Terug naar “Development”