Merge pull request #45 from RaspAP/feature/wg-extended

Extend WG functionality
This commit is contained in:
Bill Zimmerman
2021-07-13 14:03:35 +02:00
committed by GitHub
9 changed files with 779 additions and 114 deletions

View File

@@ -721,13 +721,15 @@ function validateCidr($cidr)
}
// Validates a host or FQDN
function validate_host($host) {
function validate_host($host)
{
return preg_match('/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i', $host);
}
// Gets night mode toggle value
// @return boolean
function getNightmode(){
function getNightmode()
{
if ($_COOKIE['theme'] == 'lightsout.css') {
return true;
} else {
@@ -736,7 +738,8 @@ function getNightmode(){
}
// search array for matching string and return only first matching group
function preg_only_match($pat,$haystack) {
function preg_only_match($pat,$haystack)
{
$match = "";
if(!empty($haystack) && !empty($pat)) {
if(!is_array($haystack)) $haystack = array($haystack);
@@ -754,10 +757,34 @@ function qr_encode($str)
return preg_replace('/(?<!\\\)([\":;,])/', '\\\\\1', $str);
}
function evalHexSequence($string) {
function evalHexSequence($string)
{
$evaluator = function ($input) {
return hex2bin($input[1]);
};
return preg_replace_callback('/\\\x(..)/', $evaluator, $string);
}
/* File upload callback object
*
*/
class validation
{
public function check_name_length($object)
{
if (strlen($object->file['filename']) > 255) {
$object->set_error('File name is too long.');
}
}
}
/* Resolves public IP address
*
* @return string $public_ip
*/
function get_public_ip()
{
exec('wget https://ipinfo.io/ip -qO -', $public_ip);
return $public_ip[0];
}

View File

@@ -3,6 +3,7 @@
require_once 'includes/status_messages.php';
require_once 'includes/config.php';
require_once 'includes/wifi_functions.php';
require_once 'app/lib/uploader.php';
getWifiInterface();
@@ -41,11 +42,9 @@ function DisplayOpenVPNConfig()
}
exec('pidof openvpn | wc -l', $openvpnstatus);
exec('wget https://ipinfo.io/ip -qO -', $return);
$serviceStatus = $openvpnstatus[0] == 0 ? "down" : "up";
$auth = file(RASPI_OPENVPN_CLIENT_LOGIN, FILE_IGNORE_NEW_LINES);
$public_ip = $return[0];
$public_ip = get_public_ip();
// parse client auth credentials
if (!empty($auth)) {
@@ -100,8 +99,8 @@ function DisplayOpenVPNConfig()
*/
function SaveOpenVPNConfig($status, $file, $authUser, $authPassword)
{
$tmp_ovpnclient = '/tmp/ovpnclient.ovpn';
$tmp_authdata = '/tmp/authdata';
define('KB', 1024);
$tmp_destdir = '/tmp/';
$auth_flag = 0;
try {
@@ -110,66 +109,28 @@ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword)
throw new RuntimeException('Invalid parameters');
}
// Parse returned errors
switch ($file['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
throw new RuntimeException('OpenVPN configuration file not sent');
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
throw new RuntimeException('Exceeded filesize limit');
default:
throw new RuntimeException('Unknown errors');
}
$upload = \RaspAP\Uploader\Upload::factory('ovpn',$tmp_destdir);
$upload->set_max_file_size(64*KB);
$upload->set_allowed_mime_types(array('ovpn' => 'text/plain'));
$upload->file($file);
// Validate extension
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
if ($ext != 'ovpn') {
throw new RuntimeException('Invalid file extension');
}
$validation = new validation;
$upload->callbacks($validation, array('check_name_length'));
$results = $upload->upload();
// Validate MIME type
$finfo = new finfo(FILEINFO_MIME_TYPE);
if (false === $ext = array_search(
$finfo->file($file['tmp_name']),
array(
'ovpn' => 'text/plain'
),
true
)
) {
throw new RuntimeException('Invalid file format');
}
// Validate filesize
define('KB', 1024);
if ($file['size'] > 64*KB) {
throw new RuntimeException('File size limit exceeded');
}
// Use safe filename, save to /tmp
if (!move_uploaded_file(
$file['tmp_name'],
sprintf(
'/tmp/%s.%s',
'ovpnclient',
$ext
)
)
) {
throw new RuntimeException('Unable to move uploaded file');
if (!empty($results['errors'])) {
throw new RuntimeException($results['errors'][0]);
}
// Good file upload, update auth credentials if present
if (!empty($authUser) && !empty($authPassword)) {
$auth_flag = 1;
// Move tmp authdata to /etc/openvpn/login.conf
$auth.= $authUser .PHP_EOL . $authPassword .PHP_EOL;
$tmp_authdata = $tmp_destdir .'ovpn/authdata';
$auth = $authUser .PHP_EOL . $authPassword .PHP_EOL;
file_put_contents($tmp_authdata, $auth);
chmod($tmp_authdata, 0644);
$client_auth = RASPI_OPENVPN_CLIENT_PATH.pathinfo($file['name'], PATHINFO_FILENAME).'_login.conf';
system("sudo cp $tmp_authdata $client_auth", $return);
system("sudo mv $tmp_authdata $client_auth", $return);
system("sudo rm ".RASPI_OPENVPN_CLIENT_LOGIN, $return);
system("sudo ln -s $client_auth ".RASPI_OPENVPN_CLIENT_LOGIN, $return);
if ($return !=0) {
@@ -178,14 +139,16 @@ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword)
}
// Set iptables rules and, optionally, auth-user-pass
exec("sudo /etc/raspap/openvpn/configauth.sh $tmp_ovpnclient $auth_flag " .$_SESSION['ap_interface'], $return);
$tmp_ovpn = $results['full_path'];
exec("sudo /etc/raspap/openvpn/configauth.sh $tmp_ovpn $auth_flag " .$_SESSION['ap_interface'], $return);
foreach ($return as $line) {
$status->addMessage($line, 'info');
}
// Move uploaded ovpn config from /tmp and create symlink
$client_ovpn = RASPI_OPENVPN_CLIENT_PATH.pathinfo($file['name'], PATHINFO_FILENAME).'_client.conf';
chmod($tmp_ovpnclient, 0644);
system("sudo cp $tmp_ovpnclient $client_ovpn", $return);
chmod($tmp_ovpn, 0644);
system("sudo mv $tmp_ovpn $client_ovpn", $return);
system("sudo rm ".RASPI_OPENVPN_CLIENT_CONFIG, $return);
system("sudo ln -s $client_ovpn ".RASPI_OPENVPN_CLIENT_CONFIG, $return);
@@ -201,3 +164,4 @@ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword)
return $status;
}
}

View File

@@ -10,8 +10,13 @@ function DisplayWireGuardConfig()
{
$status = new StatusMessages();
if (!RASPI_MONITOR_ENABLED) {
if (isset($_POST['savewgsettings'])) {
$optRules = $_POST['wgRules'];
$optConf = $_POST['wgCnfOpt'];
$optSrvEnable = $_POST['wgSrvEnable'];
if (isset($_POST['savewgsettings']) && $optConf == 'manual' && $optSrvEnable == 1 ) {
SaveWireGuardConfig($status);
} elseif (isset($_POST['savewgsettings']) && $optConf == 'upload' && is_uploaded_file($_FILES["wgFile"]["tmp_name"])) {
SaveWireGuardUpload($status, $_FILES['wgFile'], $optRules);
} elseif (isset($_POST['startwg'])) {
$status->addMessage('Attempting to start WireGuard', 'info');
exec('sudo /bin/systemctl start wg-quick@wg0', $return);
@@ -27,7 +32,7 @@ function DisplayWireGuardConfig()
}
}
// fetch wg config
// fetch server config
exec('sudo cat '. RASPI_WIREGUARD_CONFIG, $return);
$conf = ParseConfig($return);
$wg_srvpubkey = exec('sudo cat '. RASPI_WIREGUARD_PATH .'wg-server-public.key', $return);
@@ -39,7 +44,7 @@ function DisplayWireGuardConfig()
$wg_senabled = true;
}
// todo: iterate multiple peer configs
// fetch client config
exec('sudo cat '. RASPI_WIREGUARD_PATH.'client.conf', $preturn);
$conf = ParseConfig($preturn);
$wg_pipaddress = ($conf['Address'] == '') ? getDefaultNetValue('wireguard','peer','Address') : $conf['Address'];
@@ -55,12 +60,15 @@ function DisplayWireGuardConfig()
exec('pidof wg-crypt-wg0 | wc -l', $wgstatus);
$serviceStatus = $wgstatus[0] == 0 ? "down" : "up";
$wg_state = ($wgstatus[0] > 0);
$public_ip = get_public_ip();
echo renderTemplate(
"wireguard", compact(
"status",
"wg_state",
"serviceStatus",
"public_ip",
"optRules",
"wg_log",
"peer_id",
"wg_srvpubkey",
@@ -79,6 +87,71 @@ function DisplayWireGuardConfig()
);
}
/**
* Validates uploaded .conf file, adds iptables post-up and
* post-down rules.
*
* @param object $status
* @param object $file
* @param boolean $optRules
* @return object $status
*/
function SaveWireGuardUpload($status, $file, $optRules)
{
define('KB', 1024);
$tmp_destdir = '/tmp/';
$auth_flag = 0;
try {
// If undefined or multiple files, treat as invalid
if (!isset($file['error']) || is_array($file['error'])) {
throw new RuntimeException('Invalid parameters');
}
$upload = \RaspAP\Uploader\Upload::factory('wg',$tmp_destdir);
$upload->set_max_file_size(64*KB);
$upload->set_allowed_mime_types(array('text/plain'));
$upload->file($file);
$validation = new validation;
$upload->callbacks($validation, array('check_name_length'));
$results = $upload->upload();
if (!empty($results['errors'])) {
throw new RuntimeException($results['errors'][0]);
}
// Valid upload, get file contents
$tmp_wgconfig = $results['full_path'];
$tmp_contents = file_get_contents($tmp_wgconfig);
// Set iptables rules
if (isset($optRules) && !preg_match('/PostUp|PostDown/m',$tmp_contents)) {
$rules[] = 'PostUp = '.getDefaultNetValue('wireguard','server','PostUp');
$rules[] = 'PostDown = '.getDefaultNetValue('wireguard','server','PostDown');
$rules[] = '';
$rules = join(PHP_EOL, $rules);
$rules = preg_replace('/wlan0/m', $_SESSION['ap_interface'], $rules);
$tmp_contents = preg_replace('/^\s*$/ms', $rules, $tmp_contents, 1);
file_put_contents($tmp_wgconfig, $tmp_contents);
}
// Move processed file from tmp to destination
system("sudo mv $tmp_wgconfig ". RASPI_WIREGUARD_CONFIG, $return);
if ($return ==0) {
$status->addMessage('WireGuard configuration uploaded successfully', 'info');
} else {
$status->addMessage('Unable to save WireGuard configuration', 'danger');
}
return $status;
} catch (RuntimeException $e) {
$status->addMessage($e->getMessage(), 'danger');
return $status;
}
}
/**
* Validate user input, save wireguard configuration
*