Last active
February 21, 2022 03:22
-
-
Save phuze/a0ec4cb4a79c15ec260bb532bb6c5536 to your computer and use it in GitHub Desktop.
PHP model for interfacing with remote user management script. The remote script manages users in a Berkeley DB within a Linux environment. Read the full article: Building a Secure FTP Server — https://phuze.dev/building-a-secure-ftp-server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace MyApi\Models; | |
use phpseclib3\Net\SSH2; | |
use phpseclib3\Crypt\PublicKeyLoader; | |
/** | |
* FTP model | |
*/ | |
class FtpModel | |
{ | |
public function __construct() | |
{ | |
# load private key | |
$this->key = PublicKeyLoader::load(file_get_contents('/path/to/private/key')); | |
# arrays to hold errors and/or success | |
$this->errors = []; | |
$this->success = []; | |
} | |
/** | |
* Connect to remote FTP server. | |
* Port is 22 and Timeout is 30 seconds. | |
*/ | |
private function connectSSH() | |
{ | |
$this->ssh = new SSH2('my.domain.com', 22, 30); | |
if (!$this->ssh->login('api', $this->key)) { | |
throw new Exception('Unable to establish SSH connection.'); | |
} | |
} | |
/** | |
* Verify we have a valid username. | |
* Valid usernames may only include (a-z) and dashes (-) | |
* | |
* @param string $username | |
* @return bool | |
*/ | |
private function isValidUsername(string $username) | |
{ | |
if(preg_match('/[^a-z-]/i', $username)) { | |
return false; | |
} | |
return true; | |
} | |
/** | |
* Generate a cryptographically strong password. | |
* Resulting password length will be twice the length of | |
* the supplied length, and only include [0-9a-z] | |
* | |
* @return string | |
*/ | |
private function genPassword(int $length = 8) | |
{ | |
return bin2hex(openssl_random_pseudo_bytes($length)); | |
} | |
private function parseResult(string $stdout) | |
{ | |
# explode our stdout into individual lines | |
$lines = explode("\n", $stdout); | |
# iterate over the output lines | |
foreach($lines as $line) { | |
# capture any errors | |
if (strpos($line, 'ERROR:') !== false) { | |
$this->errors[] = $line; | |
} | |
if (strpos($line, 'SUCCESS:') !== false) { | |
$this->success[] = $line; | |
} | |
} | |
# if our success array is not empty | |
# (meaning a success line was found) | |
# then our command was successful | |
if(!empty($this->success)) { | |
return true; | |
} | |
return false; | |
} | |
public function addUser(string $username) | |
{ | |
# dont allow invalid usernames | |
if(!$this->isValidUsername($username)) { | |
throw new Exception('Invalid username. Only lowercase a-z characters allowed.'); | |
} | |
# generate a new password for the user | |
# note: only admins with local access to the FTP server | |
# may define a password manually | |
$password = $this->genPassword(); | |
# connect to FTP server using SSH | |
$this->connectSSH(); | |
# execute our remote user management script | |
$create = $this->ssh->exec("sudo /etc/vsftpd/users add {$username} {$password}"); | |
# $create will be stdout, so we'll need to parse it to | |
# confirm if the action was successful | |
$result = $this->parseResult($create); | |
# return the result | |
return [ | |
'success' => $result, | |
'message' => $result ? $this->success[0] : $this->errors[0], | |
'password' => $result ? $password : null | |
]; | |
} | |
public function delUser(string $username) | |
{ | |
# dont allow invalid usernames | |
if(!$this->isValidUsername($username)) { | |
throw new Exception('Invalid username. Only lowercase a-z characters allowed.'); | |
} | |
# connect to FTP server using SSH | |
$this->connectSSH(); | |
# execute our remote user management script | |
$delete = $this->ssh->exec("sudo /etc/vsftpd/users del {$username}"); | |
# $delete will be stdout, so we'll need to parse it to | |
# confirm if the action was successful | |
$result = $this->parseResult($delete); | |
# return the result | |
return [ | |
'success' => $result, | |
'message' => $result ? $this->success[0] : $this->errors[0] | |
]; | |
} | |
public function editUser(string $username) | |
{ | |
# dont allow invalid usernames | |
if(!$this->isValidUsername($username)) { | |
throw new Exception('Invalid username. Only lowercase a-z characters allowed.'); | |
} | |
# generate a new password for the user | |
# note: only admins with local access to the FTP server | |
# may define a password manually | |
$password = $this->genPassword(); | |
# connect to FTP server using SSH | |
$this->connectSSH(); | |
# execute our remote user management script | |
$edit = $this->ssh->exec("sudo /etc/vsftpd/users edit {$username} {$password}"); | |
# $edit will be stdout, so we'll need to parse it to | |
# confirm if the action was successful | |
$result = $this->parseResult($edit); | |
# return the result | |
return [ | |
'success' => $result, | |
'message' => $result ? $this->success[0] : $this->errors[0], | |
'password' => $result ? $password : null | |
]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment