mirror of
https://github.com/billz/raspap-webgui.git
synced 2025-03-01 10:31:47 +00:00
Merge pull request #1416 from RaspAP/feat/vpn-providers
Custom VPN provider support
This commit is contained in:
@@ -8,6 +8,7 @@ $defaults = [
|
||||
'RASPI_BRAND_TEXT' => 'RaspAP',
|
||||
'RASPI_VERSION' => '2.9.7',
|
||||
'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json',
|
||||
'RASPI_CONFIG_PROVIDERS' => 'config/vpn-providers.json',
|
||||
'RASPI_ADMIN_DETAILS' => RASPI_CONFIG.'/raspap.auth',
|
||||
'RASPI_WIFI_AP_INTERFACE' => 'wlan0',
|
||||
'RASPI_CACHE_PATH' => sys_get_temp_dir() . '/raspap',
|
||||
@@ -45,6 +46,7 @@ $defaults = [
|
||||
'RASPI_DHCP_ENABLED' => true,
|
||||
'RASPI_ADBLOCK_ENABLED' => false,
|
||||
'RASPI_OPENVPN_ENABLED' => false,
|
||||
'RASPI_VPN_PROVIDER_ENABLED' => false,
|
||||
'RASPI_WIREGUARD_ENABLED' => false,
|
||||
'RASPI_TORPROXY_ENABLED' => false,
|
||||
'RASPI_CONFAUTH_ENABLED' => true,
|
||||
|
@@ -169,6 +169,24 @@ function getDefaultNetOpts($svc,$key)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value for the specified VPN provider
|
||||
*
|
||||
* @param numeric $id
|
||||
* @param string $key
|
||||
* @return object $json
|
||||
*/
|
||||
function getProviderValue($id,$key)
|
||||
{
|
||||
$obj = json_decode(file_get_contents(RASPI_CONFIG_PROVIDERS), true);
|
||||
if ($obj === null) {
|
||||
return false;
|
||||
} else {
|
||||
$id--;
|
||||
return $obj['providers'][$id][$key];
|
||||
}
|
||||
}
|
||||
|
||||
/* Functions to write ini files */
|
||||
|
||||
/**
|
||||
@@ -669,6 +687,7 @@ function initializeApp()
|
||||
$_SESSION["theme_url"] = getThemeOpt();
|
||||
$_SESSION["toggleState"] = getSidebarState();
|
||||
$_SESSION["bridgedEnabled"] = getBridgedState();
|
||||
$_SESSION["providerID"] = getProviderID();
|
||||
}
|
||||
|
||||
function getThemeOpt()
|
||||
@@ -709,6 +728,17 @@ function getBridgedState()
|
||||
return $arrHostapdConf['BridgedEnable'];
|
||||
}
|
||||
|
||||
// Returns VPN provider ID, if defined
|
||||
function getProviderID()
|
||||
{
|
||||
if (RASPI_VPN_PROVIDER_ENABLED) {
|
||||
$arrProvider = parse_ini_file(RASPI_CONFIG.'/provider.ini');
|
||||
if (isset($arrProvider['providerID'])) {
|
||||
return $arrProvider['providerID'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the format of a CIDR notation string
|
||||
*
|
||||
|
@@ -27,6 +27,9 @@
|
||||
case "/wg_conf":
|
||||
DisplayWireGuardConfig();
|
||||
break;
|
||||
case "/provider_conf":
|
||||
DisplayProviderConfig();
|
||||
break;
|
||||
case "/torproxy_conf":
|
||||
DisplayTorProxyConfig();
|
||||
break;
|
||||
|
316
includes/provider.php
Executable file
316
includes/provider.php
Executable file
@@ -0,0 +1,316 @@
|
||||
<?php
|
||||
|
||||
require_once 'includes/config.php';
|
||||
|
||||
/*
|
||||
* Display VPN provider configuration
|
||||
*/
|
||||
function DisplayProviderConfig()
|
||||
{
|
||||
// initialize status object
|
||||
$status = new \RaspAP\Messages\StatusMessage;
|
||||
|
||||
// set defaults
|
||||
$id = $_SESSION["providerID"];
|
||||
$binPath = getProviderValue($id, "bin_path");
|
||||
$providerName = getProviderValue($id, "name");
|
||||
$providerVersion = getProviderVersion($id, $binPath);
|
||||
$installPage = getProviderValue($id, "install_page");
|
||||
$publicIP = get_public_ip();
|
||||
$serviceStatus = 'down';
|
||||
$statusDisplay = 'down';
|
||||
|
||||
if (!file_exists($binPath)) {
|
||||
$status->addMessage(sprintf(_('Expected %s binary not found at: %s'), $providerName, $binPath), 'warning');
|
||||
$status->addMessage(sprintf(_('Visit the <a href="%s" target="_blank">installation instructions</a> for %s\'s Linux CLI.'), $installPage, $providerName), 'warning');
|
||||
$ctlState = 'disabled';
|
||||
$providerVersion = 'not found';
|
||||
} elseif (empty($providerVersion)) {
|
||||
$status->addMessage(sprintf(_('Unable to execute %s binary found at: %s'), $providerName, $binPath), 'warning');
|
||||
$status->addMessage(_('Check that binary is executable and permissions exist in raspap.sudoers'), 'warning');
|
||||
$ctlState = 'disabled';
|
||||
$providerVersion = 'not found';
|
||||
} else {
|
||||
// fetch provider status
|
||||
$serviceStatus = getProviderStatus($id, $binPath);
|
||||
$statusDisplay = $serviceStatus == "down" ? "inactive" : "active";
|
||||
|
||||
// fetch provider log
|
||||
$providerLog = getProviderLog($id, $binPath, $country);
|
||||
|
||||
// fetch account info
|
||||
$accountInfo = getAccountInfo($id, $binPath, $providerName);
|
||||
$accountLink = getProviderValue($id, "account_page");
|
||||
|
||||
// fetch available countries
|
||||
$countries = getCountries($id, $binPath);
|
||||
}
|
||||
|
||||
if (!RASPI_MONITOR_ENABLED) {
|
||||
if (isset($_POST['SaveProviderSettings'])) {
|
||||
if (isset($_POST['country'])) {
|
||||
$country = trim($_POST['country']);
|
||||
if (strlen($country) == 0) {
|
||||
$status->addMessage('Select a country from the server location list', 'danger');
|
||||
} else {
|
||||
$return = saveProviderConfig($status, $binPath, $country, $id);
|
||||
}
|
||||
}
|
||||
} elseif (isset($_POST['StartProviderVPN'])) {
|
||||
$status->addMessage('Attempting to connect VPN provider', 'info');
|
||||
$cmd = getCliOverride($id, 'cmd_overrides', 'connect');
|
||||
exec("sudo $binPath $cmd", $return);
|
||||
$return = stripArtifacts($return);
|
||||
foreach ($return as $line) {
|
||||
if (strlen(trim($line)) > 0) {
|
||||
$line = preg_replace('/\e\[\?[0-9]*l\s(.*)\e.*$/', '$1', $line);
|
||||
$line = preg_replace('/\e\[0m\e\[[0-9;]*m(.*)/', '$1', $line);
|
||||
$status->addMessage($line, 'info');
|
||||
}
|
||||
}
|
||||
} elseif (isset($_POST['StopProviderVPN'])) {
|
||||
$status->addMessage('Attempting to disconnect VPN provider', 'info');
|
||||
$cmd = getCliOverride($id, 'cmd_overrides', 'disconnect');
|
||||
exec("sudo $binPath $cmd", $return);
|
||||
$return = stripArtifacts($return);
|
||||
foreach ($return as $line) {
|
||||
if (strlen(trim($line)) > 0) {
|
||||
$line = preg_replace('/\[1;33;49m(.*)\[0m/', '$1', $line);
|
||||
$status->addMessage($line, 'info');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo renderTemplate(
|
||||
"provider", compact(
|
||||
"status",
|
||||
"serviceStatus",
|
||||
"statusDisplay",
|
||||
"providerName",
|
||||
"providerVersion",
|
||||
"accountInfo",
|
||||
"accountLink",
|
||||
"countries",
|
||||
"country",
|
||||
"providerLog",
|
||||
"publicIP",
|
||||
"ctlState"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates VPN provider settings
|
||||
*
|
||||
* @param object $status
|
||||
* @param string $binPath
|
||||
* @param string $country
|
||||
* @param integer $id (optional)
|
||||
*/
|
||||
function saveProviderConfig($status, $binPath, $country, $id = null)
|
||||
{
|
||||
$status->addMessage(sprintf(_('Attempting to connect to %s'),$country), 'info');
|
||||
$cmd = getCliOverride($id, 'cmd_overrides', 'connect');
|
||||
// mullvad requires relay set location before connect
|
||||
if ($id == 2) {
|
||||
exec("sudo $binPath relay set location $country", $return);
|
||||
exec("sudo $binPath $cmd", $return);
|
||||
} else {
|
||||
exec("sudo $binPath $cmd $country", $return);
|
||||
}
|
||||
$return = stripArtifacts($return);
|
||||
foreach ($return as $line) {
|
||||
if ( strlen(trim($line)) >0 ) {
|
||||
$status->addMessage($line, 'info');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes artifacts from shell_exec string values
|
||||
*
|
||||
* @param string $output
|
||||
* @param string $pattern
|
||||
* @return string $result
|
||||
*/
|
||||
function stripArtifacts($output, $pattern = null)
|
||||
{
|
||||
$result = preg_replace('/[-\/\n\t\\\\'.$pattern.'|]/', '', $output);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an override for provider CLI
|
||||
*
|
||||
* @param integer $id
|
||||
* @param string $group
|
||||
* @param string $item
|
||||
* @return string $override
|
||||
*/
|
||||
function getCliOverride($id, $group, $item)
|
||||
{
|
||||
$obj = json_decode(file_get_contents(RASPI_CONFIG_PROVIDERS), true);
|
||||
if ($obj === null) {
|
||||
return false;
|
||||
} else {
|
||||
$id--;
|
||||
if ($obj['providers'][$id][$group][$item] === null) {
|
||||
return $item;
|
||||
} else {
|
||||
return $obj['providers'][$id][$group][$item];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreives VPN provider status
|
||||
*
|
||||
* @param integer $id
|
||||
* @param string $binPath
|
||||
* @return string $status
|
||||
*/
|
||||
function getProviderStatus($id, $binPath)
|
||||
{
|
||||
$cmd = getCliOverride($id, 'cmd_overrides', 'status');
|
||||
$pattern = getCliOverride($id, 'regex', 'status');
|
||||
exec("sudo $binPath $cmd", $cmd_raw);
|
||||
$cmd_raw = strtolower(stripArtifacts($cmd_raw[0]));
|
||||
|
||||
if (!empty($cmd_raw[0])) {
|
||||
if (preg_match($pattern, $cmd_raw, $match)) {
|
||||
$status = "down";
|
||||
} else {
|
||||
$status = "up";
|
||||
}
|
||||
} else {
|
||||
$status = "down";
|
||||
}
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves available countries
|
||||
*
|
||||
* @param integer $id
|
||||
* @param string $binPath
|
||||
* @return array $countries
|
||||
*/
|
||||
function getCountries($id, $binPath)
|
||||
{
|
||||
$countries = [];
|
||||
$cmd = getCliOverride($id, 'cmd_overrides', 'countries');
|
||||
$pattern = getCliOverride($id, 'regex', 'pattern');
|
||||
$replace = getCliOverride($id, 'regex', 'replace');
|
||||
$slice = getCliOverride($id, 'regex', 'slice');
|
||||
exec("sudo $binPath $cmd", $output);
|
||||
|
||||
// CLI country output differs considerably between different providers.
|
||||
// Ideally, custom parsing would be avoided in favor of a pure regex solution
|
||||
switch ($id) {
|
||||
case 1: // expressvpn
|
||||
$output = array_slice($output, $slice);
|
||||
foreach ($output as $item) {
|
||||
$item = preg_replace($pattern, $replace, $item);
|
||||
$parts = explode(',', $item);
|
||||
$key = trim($parts[0]);
|
||||
$value = trim($parts[1]);
|
||||
$countries[$key] = $value;
|
||||
}
|
||||
break;
|
||||
case 2: // mullvad
|
||||
foreach ($output as $item) {
|
||||
$item = preg_replace($pattern, $replace, $item);
|
||||
if (strlen(trim($item) >0)) {
|
||||
preg_match('/\s+([a-z0-9-]+)\s.*$/', $item, $match);
|
||||
if (count($match) > 1) {
|
||||
$key = $match[1];
|
||||
$item = str_pad($item, strlen($item)+16,' ', STR_PAD_LEFT);
|
||||
$countries[$key] = $item;
|
||||
} else {
|
||||
preg_match('/\(([a-z]+)\)/', $item, $match);
|
||||
$key = $match[1];
|
||||
if (strlen($match[1]) == 3) {
|
||||
$item = str_pad($item, strlen($item)+8,' ', STR_PAD_LEFT);
|
||||
}
|
||||
$countries[$key] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3: // nordvpn
|
||||
$output = stripArtifacts($output,'\s');
|
||||
$arrTmp = explode(",", $output[0]);
|
||||
$countries = array_combine($arrTmp, $arrTmp);
|
||||
foreach ($countries as $key => $value) {
|
||||
$countries[$key] = str_replace("_", " ", $value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
$select = array(' ' => _("Select a country..."));
|
||||
$countries = $select + $countries;
|
||||
return $countries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves provider log
|
||||
*
|
||||
* @param integer $id
|
||||
* @param string $binPath
|
||||
* @param string $country
|
||||
* @return string $log
|
||||
*/
|
||||
function getProviderLog($id, $binPath, &$country)
|
||||
{
|
||||
$cmd = getCliOverride($id, 'cmd_overrides', 'log');
|
||||
exec("sudo $binPath $cmd", $cmd_raw);
|
||||
$output = stripArtifacts($cmd_raw);
|
||||
foreach ($output as $item) {
|
||||
if (preg_match('/Country: (\w+)/', $item, $match)) {
|
||||
$country = $match[1];
|
||||
}
|
||||
$providerLog.= ltrim($item) .PHP_EOL;
|
||||
}
|
||||
return $providerLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves provider version information
|
||||
*
|
||||
* @param integer $id
|
||||
* @param string $binPath
|
||||
* @return string $version
|
||||
*/
|
||||
function getProviderVersion($id, $binPath)
|
||||
{
|
||||
$cmd = getCliOverride($id, 'cmd_overrides', 'version');
|
||||
$version = shell_exec("sudo $binPath $cmd");
|
||||
$version = preg_replace('/^[^\w]+\s*/', '', $version);
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves provider account info
|
||||
*
|
||||
* @param integer $id
|
||||
* @param string $binPath
|
||||
* @param string $providerName
|
||||
* @return array
|
||||
*/
|
||||
function getAccountInfo($id, $binPath, $providerName)
|
||||
{
|
||||
$cmd = getCliOverride($id, 'cmd_overrides', 'account');
|
||||
exec("sudo $binPath $cmd", $acct);
|
||||
|
||||
foreach ($acct as &$item) {
|
||||
$item = preg_replace('/^[^\w]+\s*/', '', $item);
|
||||
}
|
||||
if (empty($acct)) {
|
||||
$msg = sprintf(_("Account information not available from %s's Linux CLI."), $providerName);
|
||||
$acct[] = $msg;
|
||||
}
|
||||
return $acct;
|
||||
}
|
||||
|
@@ -60,6 +60,11 @@
|
||||
<a class="nav-link" href="wg_conf"><span class="ra-wireguard mr-2"></span><span class="nav-label"><?php echo _("WireGuard"); ?></a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (RASPI_VPN_PROVIDER_ENABLED) : ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="provider_conf"><i class="fas fa-shield-alt fa-fw mr-2"></i><span class="nav-label"><?php echo _(getProviderValue($_SESSION["providerID"], "name")); ?></a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (RASPI_TORPROXY_ENABLED) : ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="torproxy_conf"><i class="fas fa-eye-slash fa-fw mr-2"></i><span class="nav-label"><?php echo _("TOR proxy"); ?></a>
|
||||
|
Reference in New Issue
Block a user