src/Config.php
<?php
namespace OpenVPN;
use OpenVPN\Interfaces\ConfigInterface;
use OpenVPN\Interfaces\GeneratorInterface;
use RuntimeException;
use function in_array;
use function is_bool;
/**
* Class Config
*
* @property string $modeSet OpenVPN major mode: p2p, server
* @property string $local Local host name or IP address
* @property string $remote Remote host name or IP address
* @property bool|null $remoteRandom When multiple --remote address/ports are specified, initially randomize the order of the list
* @property string $proto Protocol for communicating with remote host: tcp, udp, tcp-client
* @property integer $connectRetry For --proto tcp-client, take n as the number of seconds to wait between connection retries (default=5)
* @property string $httpProxy Connect to remote host through an HTTP proxy
* @property bool|null $httpProxyRetry Retry indefinitely on HTTP proxy errors. If an HTTP proxy error occurs, simulate a SIGUSR1 reset.
* @property integer $httpProxyTimeout Set proxy timeout to n seconds, default=5.
* @property string $httpProxyOption Set extended HTTP proxy options
* @property string $socksProxy Connect to remote host through a Socks5 proxy
* @property bool|null $socksProxyRetry Retry indefinitely on Socks proxy errors. If a Socks proxy error occurs, simulate a SIGUSR1 reset.
* @property string $resolvRetry If hostname resolve fails for --remote, retry resolve for n seconds before failing
* @property bool|null $float Allow remote peer to change its IP address and/or port number, such as due to DHCP
* @property string $ipchange Execute shell command, format: cmd ip_address port_number
* @property integer $port TCP/UDP port number for both local and remote: 1194
* @property integer $lport TCP/UDP port number for local
* @property integer $rport TCP/UDP port number for remote
* @property bool|null $nobind Do not bind to local address and port
* @property string $dev TUN/TAP virtual network device: tunX, tapX, null
* @property string $devType Which device type are we using? device-type should be tun or tap
* @property bool|null $tunIpv6 Build a tun link capable of forwarding IPv6 traffic
* @property string $devNode Explicitly set the device node rather than using /dev/net/tun, /dev/tun, /dev/tap, etc.
* @property string $ifconfig Set TUN/TAP adapter IP address of the local VPN endpoint.
* @property bool|null $ifconfigNoexec Don’t actually execute ifconfig/netsh commands, instead pass –ifconfig parameters to scripts using environmental variables.
* @property bool|null $ifconfigNowarn Don’t output an options consistency check warning
* @property string $ifconfigPool Set aside a pool of subnets to be dynamically allocated to connecting clients, similar to a DHCP server.
* @property string $ifconfigPoolPersist Persist/unpersist ifconfig-pool data to file, at secondsintervals (default=600),
* @property string $cipher Encrypt packets with cipher algorithm alg. The default is BF-CBC,an abbreviation for Blowfish in Cipher Block Chaining mode.
* @property bool|null $redirectGateway
* @property integer $keyDirection
* @property string $remoteCertTls
* @property string $auth Authenticate packets with HMAC using message digest algorithm alg. (The default is SHA1).
* @property bool|null $authUserPass
* @property bool|null $authNocache
* @property string $authUserPassVerify Path to login script
* @property bool|null $duplicateCn You may need this if everyone is using same certificate
* @property bool|null $persistKey
* @property bool|null $persistTun
* @property bool|null $compLzo Use fast LZO compression — may add up to 1 byte per packet for incompressible data.
* @property bool|null $compNoadapt When used in conjunction with –comp-lzo, this option will disable OpenVPN’s adaptive compression algorithm.
* @property integer $verb Set output verbosity to n(default=1). Each level shows all info from the previous levels: 0,1,2 ... 11
* @property string $server A helper directive designed to simplify the configuration of OpenVPN’s server mode.
* @property string $serverBridge A helper directive similar to --server which is designed to simplify the configuration of OpenVPN’s server mode in ethernet bridging configurations.
* @property string $keepalive
* @property integer $renegSec
* @property string $user
* @property string $group
* @property string $mute
* @property string $status
* @property string $logAppend
* @property string $clientConfigDir
* @property string $scriptSecurity
* @property string $usernameAsCommonName
* @property string $verifyClientCert
*
* @package OpenVPN
*/
class Config implements ConfigInterface, GeneratorInterface
{
/**
* List of types of certs, for validation
*/
public const ALLOWED_TYPES_OF_CERTS = [
'ca',
'cert',
'key',
'dh',
'tls-auth',
'secret',
'pkcs12'
];
/**
* Array with all certificates
*
* @var array
*/
private $certs = [];
/**
* List of all routes available on server
*
* @var array
*/
private $routes = [];
/**
* List of lines which must be pushed to clients
*
* @var array
*/
private $pushes = [];
/**
* List of lines which can be used as remotes
*
* @var array
*/
private $remotes = [];
/**
* All parameters added via addParam method
*
* @var array
*/
private $parameters = [];
/**
* Config constructor.
*
* @param array $parameters List of default parameters
*/
public function __construct(array $parameters = [])
{
$this->setParams($parameters);
}
/**
* Alias for client line of config
*
* @return \OpenVPN\Interfaces\ConfigInterface
*/
public function client(): ConfigInterface
{
return $this->set('client');
}
/**
* Import content of all listed certificates
*
* @return void
*/
public function loadCertificates(): void
{
foreach ($this->certs as &$cert) {
$cert['content'] = rtrim(file_get_contents($cert['path']));
}
}
/**
* Add new cert into the configuration
*
* @param string $type Type of certificate [ca, cert, key, dh, tls-auth]
* @param string $path Absolute or relative path to certificate or content of this file
* @param bool|null $isContent If true, then script will try to load file from dist by $path
*
* @return \OpenVPN\Interfaces\ConfigInterface
* @throws \RuntimeException
*/
public function setCert(string $type, string $path, bool $isContent = null): ConfigInterface
{
$type = mb_strtolower($type);
Helpers::isCertAllowed($type);
if (true === $isContent) {
$this->certs[$type]['content'] = $path;
} else {
$this->certs[$type]['path'] = $path;
}
return $this;
}
/**
* Return information about specified certificate
*
* @param string $type
*
* @return array
* @throws \RuntimeException
*/
public function getCert(string $type): array
{
$type = mb_strtolower($type);
Helpers::isCertAllowed($type);
return $this->certs[$type] ?? [];
}
/**
* Append new push into the array
*
* @param string $line String with line which must be pushed
*
* @return \OpenVPN\Interfaces\ConfigInterface
*/
public function setPush(string $line): ConfigInterface
{
$this->pushes[] = trim($line, '"');
return $this;
}
/**
* Remove route line from push array
*
* @param string $line String with line which must be pushed
*
* @return \OpenVPN\Interfaces\ConfigInterface
*/
public function unsetPush(string $line): ConfigInterface
{
unset($this->pushes[$line]);
return $this;
}
/**
* Append new route into the array
*
* @param string $line String with route
*
* @return \OpenVPN\Interfaces\ConfigInterface
*/
public function setRoute(string $line): ConfigInterface
{
$this->routes[] = trim($line, '"');
return $this;
}
/**
* Remove route line from routes array
*
* @param string $line String with route
*
* @return \OpenVPN\Interfaces\ConfigInterface
*/
public function unsetRoute(string $line): ConfigInterface
{
unset($this->routes[$line]);
return $this;
}
/**
* Append new push into the array
*
* @param string $line String with line which must be added as remote
*
* @return \OpenVPN\Interfaces\ConfigInterface
*/
public function setRemote(string $line): ConfigInterface
{
$this->remotes[] = trim($line, '"');
return $this;
}
/**
* Remove remote line from remotes array
*
* @param string $line String with line which must be added as remote
*
* @return \OpenVPN\Interfaces\ConfigInterface
*/
public function unsetRemote(string $line): ConfigInterface
{
unset($this->remotes[$line]);
return $this;
}
/**
* Add some new parameter to the list of parameters
*
* @param string $name Name of parameter
* @param string|bool|null $value Value of parameter
*
* @return \OpenVPN\Interfaces\ConfigInterface
* @example $this->add('client')->add('remote', 'vpn.example.com');
*/
public function set(string $name, $value = null): ConfigInterface
{
$name = mb_strtolower($name);
// Check if key is certificate or push, or classic parameter
if (in_array($name, self::ALLOWED_TYPES_OF_CERTS, true)) {
return $this->setCert($name, $value);
}
// If is push then use add push method
if ($name === 'remote') {
return $this->setRemote($value);
}
// If is push then use add push method
if ($name === 'push') {
return $this->setPush($value);
}
// If is push then use add push method
if ($name === 'route') {
return $this->setRoute($value);
}
// Check if provided value is boolean and if it's true, then set null (that mean parameter without value)
if (is_bool($value) && $value) {
if ($value) {
$value = null;
} else {
// If false then skip this step
return $this;
}
}
// Set new value
$this->parameters[$name] = $value;
return $this;
}
/**
* Get some custom element
*
* @param string|null $name Name of parameter
*
* @return mixed
*/
public function get(string $name)
{
return $this->parameters[$name] ?? null;
}
/**
* Generate config by parameters in memory
*
* @param string $type Type of generated config: raw (default), json
*
* @return array|string|null
*/
public function generate(string $type = 'raw')
{
$generator = new Generator($this);
return $generator->generate($type);
}
/**
* @param string $name
*
* @return bool
*/
public function __isset(string $name): bool
{
// Inform about deleting push
if ($name === 'push') {
throw new RuntimeException("Not possible to remove push, use 'unsetPush' instead");
}
// Inform about deleting route
if ($name === 'route') {
throw new RuntimeException("Not possible to remove route, use 'unsetRoute' instead");
}
// Inform about deleting route
if ($name === 'remote') {
throw new RuntimeException("Not possible to remove remote, use 'unsetRemote' instead");
}
return isset($this->parameters[$name]);
}
/**
* @param string $name
* @param string|bool|integer|null $value
*/
public function __set(string $name, $value = null): void
{
$name = Helpers::decamelize($name);
$this->set($name, $value);
}
/**
* @param string $name
*
* @return string|bool|null
*/
public function __get(string $name)
{
return $this->get($name);
}
/**
* Remove some parameter from array by name
*
* @param string $name Name of parameter
*
* @return void
* @throws \RuntimeException
*/
public function __unset(string $name): void
{
// Inform about deleting push
if ($name === 'push') {
throw new RuntimeException("Not possible to remove push, use 'unsetPush' instead");
}
// Inform about deleting route
if ($name === 'route') {
throw new RuntimeException("Not possible to remove route, use 'unsetRoute' instead");
}
// Inform about deleting route
if ($name === 'remote') {
throw new RuntimeException("Not possible to remove remote, use 'unsetRemote' instead");
}
// Check if key is certificate or push, or classic parameter
if (in_array($name, self::ALLOWED_TYPES_OF_CERTS, true)) {
$this->unsetCert($name);
return;
}
// Update list of parameters
$this->parameters = array_map(
static function ($param) use ($name) {
return ($param['name'] === $name) ? null : $param;
},
$this->parameters
);
}
/**
* Remove selected certificate from array
*
* @param string $type Type of certificate [ca, cert, key, dh, tls-auth]
*
* @return \OpenVPN\Interfaces\ConfigInterface
* @throws \RuntimeException
*/
public function unsetCert(string $type): ConfigInterface
{
$type = mb_strtolower($type);
Helpers::isCertAllowed($type);
unset($this->certs[$type]);
return $this;
}
/**
* Set scope of certs
*
* @param \OpenVPN\Types\Cert[]|string[] $certs
* @param bool $loadCertificates
*
* @return \OpenVPN\Interfaces\ConfigInterface
*/
public function setCerts(array $certs, bool $loadCertificates = false): ConfigInterface
{
// Pass list of certs from array to variable
foreach ($certs as $type => $path) {
$this->setCert($type, $path);
}
// If need to load content of files from disk
if ($loadCertificates) {
$this->loadCertificates();
}
return $this;
}
/**
* Set scope of unique pushes
*
* @param \OpenVPN\Types\Push[]|string[] $pushes
*
* @return \OpenVPN\Interfaces\ConfigInterface
*/
public function setPushes(array $pushes): ConfigInterface
{
foreach ($pushes as $push) {
$this->setPush($push);
}
return $this;
}
/**
* Set scope of unique routes
*
* @param \OpenVPN\Types\Route[]|string[] $routes
*
* @return \OpenVPN\Interfaces\ConfigInterface
*/
public function setRoutes(array $routes): ConfigInterface
{
foreach ($routes as $route) {
$this->setRoute($route);
}
return $this;
}
/**
* Set scope of unique remotes
*
* @param \OpenVPN\Types\Remote[]|string[] $remotes
*
* @return \OpenVPN\Interfaces\ConfigInterface
*/
public function setRemotes(array $remotes): ConfigInterface
{
foreach ($remotes as $remote) {
$this->setRemote($remote);
}
return $this;
}
/**
* Set scope of unique parameters
*
* @param \OpenVPN\Types\Parameter[]|string[] $parameters
*
* @return \OpenVPN\Interfaces\ConfigInterface
*/
public function setParams(array $parameters): ConfigInterface
{
foreach ($parameters as $name => $value) {
$this->set($name, $value);
}
return $this;
}
/**
* Export array of all certificates
*
* @return array
*/
public function getCerts(): array
{
return $this->certs;
}
/**
* Export array of all pushes
*
* @return array
*/
public function getPushes(): array
{
return $this->pushes;
}
/**
* Export array of all routes
*
* @return array
*/
public function getRoutes(): array
{
return $this->routes;
}
/**
* Export array of all remotes
*
* @return array
*/
public function getRemotes(): array
{
return $this->remotes;
}
/**
* Export array of all parameters
*
* @return array
*/
public function getParameters(): array
{
return $this->parameters;
}
}