Yet Another Concept of Custom Manageable FTP (Part II)

Hi! The article of the custom manageable FTP is now continue. To read previous part content, please click on this link (http://paparadit.blogspot.com/2012/04/yet-another-concept-of-custom.html). It will explained step-by-step about setting up virtual users on FTP server. To make your mind clear, if you have 10 FTP servers, the setting must installed in each machine.

A most tricky part is the authentication & uploader sub-routines. Before I explain it technically, here is a pseudo-concept:

  1. Provide users DB on a opt. DB Server.
  2. Provide a single HTTP Server for user authentication.
  3. Once a user has been successfully authenticated as well in front-end system, get the IP of FTP Server where they allowed to make a link.
  4. Also create immediately the FTP user using htpasswd feature and create all of the FTP environments (including home directory and it's permissions).
  5. Try to adding information to the file uploaded from client or some kind like that to securing it. Eg: a complete date string attached to file name will avoid it replaced by the same file or try to replace an empty space in file name into "_" characters, etc, etc.
  6. If there's file uploaded from user, make a direct stream connection to FTP server or IP address got from point #3, using ftp_connect, ftp_login and ftp_put (with additional FTP_AUTORESUME parameter).
  7. After file transferred successfully, move it to final destination.
  8. Freeing all buffer before quitting to save memory.

So, on this experiment, I currently using 3 MySQL tables; the user table, the ftp server mapping table & logging table. Take a look at below user table (m_users):


The table contain 3 fields only. The branch field indicate the unit code referring where the user come from. Create a dummy record on it like below example picture:


While ftp server mapping table only contains 2 fields (m_ftp).


The branch_grp field, contains 2 digit left of the unit code. It's described where the dedicated FTP will take place. Insert 3 dummy records as shown on below picture, as we'll use a real 10.6.1.19 machine:


The last table (m_log), purposed for transaction logging. Replacing a common /var/log/vsftpd.log file. The records will auto-inserted later by the system.


Next, let's create UI form. This form contains user ID, password and a file type object, also a submit button.

So, when submit button pressed, the system will executing authentication to database & creating FTP users in realtime mode. The creation of realtime FTP users will be made from SSH connections - automatically. So, normally we need libssh2.so and OpenSSL (http://www.php.net/manual/en/book.ssh2.php) natively installed on the web server. If your server luckily have it, test it with phpinfo files. It will displayed like below picture:


Anyway, if you found difficult to installed it manually, you'll no find anything on phpinfo file.


Since the installation of libssh2.so (and all of the dependencies) will takes time and too much complicated, so I'll using third party libraries which give the same basic function (and it's much easier). It named as phpseclib. Download it & place it to lib folder under the web server (/var/www/html/lib).

First, try the demo files. If it failed to upload a file to one of FTP server, then take a look at Linux firewall and SELinux configuration by running setup in console.


Even it show disabled, you still need to read /etc/selinux/config file exactly. In my experiments - weirdly - it's shows that the SELinux still in enforcing mode:


So, you must edit it to "disable":


Save it, reboot and make sure that the phpseclib demo file running successfully. Only if you're sure that all above tested and worked 100%, then, here below what belong on core PHP file (the front-end system):

<?
if (isset($_POST['SUBMIT']))
{
// DB initialization
define("DB_HOST", "db.host.or.ip");
define("DB_USER", "db_user");
define("DB_PASS", "db_pass");
define("DB_NAME", "db_ftp");
$link=mysql_connect(DB_HOST,DB_USER,DB_PASS);
mysql_select_db(DB_NAME);
$usr=$_POST['usr'];
$passwd=$_POST['passwd'];
if ($_FILES['filenya']['size']!=0) $imageexist=true;
else $imageexist=false;

// HTTP authentication
$r=mysql_query("select * from m_users where usr='$usr' and passwd='$passwd'");
$num=mysql_num_rows($r);
if (($num==1) && ($imageexist))
{
while ($row=mysql_fetch_array($r)) {
$branch=$row['branch']; }

$r=mysql_query("select * from m_ftp where branch_grp=left('$branch',2)");
while ($row=mysql_fetch_array($r)) {
$ip_ftp=$row['ip_ftp'];
$branch_grp='kan' . $row['branch_grp']; }

$ftp_user_name=$usr;
$ftp_user_pass=$passwd;
$ftp_server=$ip_ftp;
$imagefile=$_FILES['filenya']['tmp_name'];
$imagefile_name=strtolower($_FILES['filenya']['name']);
$imagefile_name=str_replace(' ','_',$imagefile_name);
$imagefile_name=date ('Y-m-d',mktime(date('H'),date('i'),date('s'),date('m'),date('d'),date('Y'))) . '_' . $imagefile_name;
$destination_file=$imagefile_name;

// create virtual users to selected FTP
include('lib/phpseclib/SSH2.php');
$ssh = new Net_SSH2($ftp_server);
if (!$ssh->login('root', 'ftp_svr_password')) exit('Login Failed');
$ssh->exec('htpasswd -b /etc/vsftpd/vsftpd.passwd ' . $ftp_user_name . ' ' . $ftp_user_pass);
$ssh->exec('mkdir /var/www/users/' . $ftp_user_name);
$ssh->exec('chown vsftpd:ftp_users /var/www/users/' . $ftp_user_name);

// connect, login, and transfer the file
$conn_id = ftp_connect($ftp_server);
$login_result = ftp_login($conn_id, $ftp_user_name, $ftp_user_pass);
$upload = ftp_put($conn_id, $destination_file, $imagefile, FTP_BINARY, FTP_AUTORESUME);

// check it if succeed
if (ftp_size($conn_id,$destination_file)!=0) {
mysql_query("insert into m_log values('$ftp_user_name','$destination_file',now())");
$ssh->exec('mv /var/www/users/' . $ftp_user_name . '/' . $destination_file . ' /var/www/users/' . $branch_grp);
echo("<script>alert('File $destination_file OK!')</script>"); }
else echo("<script>alert('Failed to upload $imagefile_name')</script>");

// freeing buffer
mysql_free_result($r);
$ssh->exec('htpasswd -D /etc/vsftpd/vsftpd.passwd ' . $ftp_user_name);
$ssh->exec('rmdir /var/www/users/' . $ftp_user_name);
$ssh->exec('exit');
ftp_close($conn_id);
}
else echo("<script>alert('Authentication failed or no file uploaded!')</script>");
}
?>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>FTP Concept</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body>
<form action="index.php" method="post" enctype="multipart/form-data" name="form1">
<table width="100%" border="0" cellspacing="1" cellpadding="1">
<tr bgcolor="#000000"> <td colspan="3">
<font color="#FFFFFF">&#8226; <strong><font size="2" face="Geneva, Arial, Helvetica, sans-serif">
Manageable FTP with PHP (&copy; 2012 by Eko Wahyudiharto)</font></strong></font></td> </tr>
<tr> <td width="11%"><font size="2" face="Geneva, Arial, Helvetica, sans-serif">User Name</font></td>
<td width="2%"><font size="2" face="Geneva, Arial, Helvetica, sans-serif">:</font></td>
<td width="87%"><input name="usr" type="text" id="usr"></td> </tr>
<tr> <td><font size="2" face="Geneva, Arial, Helvetica, sans-serif">Password</font></td>
<td><font size="2" face="Geneva, Arial, Helvetica, sans-serif">:</font></td>
<td><input name="passwd" type="password" id="passwd"></td> </tr>
<tr> <td><font size="2" face="Geneva, Arial, Helvetica, sans-serif">File to Upload</font></td>
<td><font size="2" face="Geneva, Arial, Helvetica, sans-serif">:</font></td>
<td><input name="filenya" type="file" id="filenya"></td> </tr>
<tr> <td>&nbsp;</td> <td>&nbsp;</td>
<td><input type="submit" name="SUBMIT" value="Submit"></td> </tr> </table>
</form>
</body>
</html>

Save it and run it from browser. Try to upload a file and press submit button:


If there's nothing goes wrong, then my concept is worked perfectly. Take a look at picture below:


It explained that the realtime FTP user generator perfectly creating user P81000. But note that the files uploaded now has move to "kan03" folder (indicating that it could serve as it planned):


Last, take a look at m_log table, here's a record inserted when the file has successfully uploaded. Voila!:


Conclusion:
This concept is tested, worked perfectly and it's ready to use. Even you may add some others functionality to enhance the security it self or others, including the hit performance handle or another environments such as the hardware availability. Have a mayday and please tell me your story on a box below. Thanks.

Labels: , , ,


PS: If you've benefit from this blog,
you can support it by making a small contribution.

Enter your email address to receive feed update from this blog:

Post a Comment

 

Post a Comment

Leave comments here...