diff --git a/pfSense-pkg-API/files/etc/inc/api/endpoints/APIServicesOpenVPNServer.inc b/pfSense-pkg-API/files/etc/inc/api/endpoints/APIServicesOpenVPNServer.inc new file mode 100644 index 000000000..c82299ec8 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/endpoints/APIServicesOpenVPNServer.inc @@ -0,0 +1,38 @@ +url = "/api/v1/services/openvpn/server"; + } + + protected function get() { + return (new APIServicesOpenVPNServerRead())->call(); + } + + protected function post() { + return (new APIServicesOpenVPNServerCreate())->call(); + } + + protected function put() { + return (new APIServicesOpenVPNServerUpdate())->call(); + } + + protected function delete() { + return (new APIServicesOpenVPNServerDelete())->call(); + } +} diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc index 01895e57a..1c3d09a0b 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc @@ -1229,6 +1229,300 @@ function get($id, $data=[], $all=false) { "return" => $id, "message" => "Please check the system_logs, the WOL command did not complete successfully" ], + 2104 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Mode" + ], + 2105 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Protocol" + ], + 2106 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Device mode" + ], + 2107 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Interface" + ], + 2108 => [ + "status" => "server error", + "code" => 500, + "return" => $id, + "message" => "Invalid or taken OpenVPN Server Local port (allowed range 1-65535)" + ], + 2109 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server TLS Key Usage Mode" + ], + 2110 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server TLS keydir direction" + ], + 2111 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server TLS DH Parameter Length" + ], + 2112 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server ECDH Curve" + ], + 2113 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Certificate Depth" + ], + 2114 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Fallback Data Encryption Algorithm" + ], + 2115 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Auth Digest Algorithm" + ], + 2116 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Hardware Crypto Engine" + ], + 2117 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown or already in use OpenVPN Server Tunnel Network" + ], + 2118 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Remote network(s)" + ], + 2119 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Local network(s)" + ], + 2120 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Allow Compression" + ], + 2121 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Compression" + ], + 2122 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Topology" + ], + 2123 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Ping Method" + ], + 2124 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Keepalive Interval" + ], + 2125 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Keepalive Timeout" + ], + 2126 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Ping Seconds" + ], + 2127 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Ping restart or exit seconds" + ], + 2128 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Ping restart or exit" + ], + 2129 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Exit Notify" + ], + 2130 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Send/Receive Buffer" + ], + 2131 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Gateway Creation" + ], + 2132 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Verbosity Level" + ], + 2133 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Concurrent Connections" + ], + 2134 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Inactive Time" + ], + 2135 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Missing OpenVPN Server Certificate" + ], + 2136 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Missing OpenVPN Server Certificate Authority" + ], + 2137 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Peer Certificate Revocation List" + ], + 2138 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Data Encryption Algorithms" + ], + 2139 => [ + "status" => "Not found", + "code" => 404, + "return" => $id, + "message" => "Unknown OpenVPN Server 'vpnid'" + ], + 2140 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Missing OpenVPN Server 'vpnid'. This parameter is needed to identify the server to modify/delete." + ], + 2141 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Missing OpenVPN Server 'shared_key'. This parameter is needed for server mode 'p2p_shared_key'" + ], + 2142 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Certificate Authority" + ], + 2143 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Certificate" + ], + 2144 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "OpenVPN Server No Certificate Authority found" + ], + 2145 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "OpenVPN Server No Server Certificate found" + ], + 2146 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Bridge Interface" + ], + 2147 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Bridge Route Gateway requires a valid Bridge Interface" + ], + 2148 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown OpenVPN Server Bridge DHCP Start and End must both be empty, or defined, or The Server Bridge DHCP range is invalid (start higher than end)." + ], + 2149 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "OpenVPN Server The field 'NTP Server' must contain a valid IP address." + ], + 2150 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "OpenVPN Server The field 'DNS Server' must contain a valid IP address." + ], + 2151 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "OpenVPN Server The field 'WINS Server' must contain a valid IP address." + ], + 2152 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "OpenVPN Server cannot delete an OpenVPN instance while the interface is assigned. Remove the interface assignment first." + ], 2999 => [ "status" => "bad request", "code" => 400, diff --git a/pfSense-pkg-API/files/etc/inc/api/models/APIServicesOpenVPNServerCreate.inc b/pfSense-pkg-API/files/etc/inc/api/models/APIServicesOpenVPNServerCreate.inc new file mode 100644 index 000000000..7ca43e678 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/models/APIServicesOpenVPNServerCreate.inc @@ -0,0 +1,1400 @@ +privileges = ["page-all", "page-openvpn-server"]; + $this->change_note = "Added OpenVPN server via API"; + } + + public function action() { + # Initialize expected configuration arrays + $this->__init_config(); + + # Add our Server to the configuration and resync OpenVPN to use new server + $this->config['openvpn']['openvpn-server'][] = $this->validated_data; + $this->write_config(); + openvpn_resync('server', $this->validated_data); + return APIResponse\get(0, $this->validated_data); + } + + private function __validate_vpnid() { + # Get next vpnid + $this->validated_data["vpnid"] = (string) openvpn_vpnid_next(); + } + + private function __validate_mode() { + # Local variables + $key_types = ["p2p_tls", "p2p_shared_key", "server_tls", "server_user", "server_tls_user"]; + + # Validate Server mode + if (isset($this->initial_data["mode"])) { + if (in_array($this->initial_data["mode"], $key_types)) { + $this->validated_data["mode"] = $this->initial_data["mode"]; + } else { + $this->errors[] = APIResponse\get(2104); + } + # Assume default if none was specified + } else { + $this->validated_data["mode"] = "p2p_tls"; + } + } + + private function __validate_authmode() { + # Validate Backend for authentication + if (isset($this->initial_data["authmode"])) { + $this->validated_data["authmode"] = $this->initial_data["authmode"]; + # Assume default if none was specified + } else { + $this->validated_data["authmode"] = "Local Database"; + } + } + + private function __validate_protocol() { + # Local variables + $key_types = ["UDP4", "UDP6", "TCP4", "TCP6", "UDP", "TCP"]; + + # Validate Protocol + if (isset($this->initial_data["protocol"])) { + if (in_array(strtoupper($this->initial_data["protocol"]), $key_types)) { + $this->validated_data["protocol"] = strtoupper($this->initial_data["protocol"]); + } else { + $this->errors[] = APIResponse\get(2105); + } + # Assume default if none was specified + } else { + $this->validated_data["protocol"] = "UDP4"; + } + } + + private function __validate_dev_mode() { + # Local variables + $key_types = ["tun", "tap"]; + + # Validate Device mode + if (isset($this->initial_data["dev_mode"])) { + if (in_array($this->initial_data["dev_mode"], $key_types)) { + $this->validated_data["dev_mode"] = $this->initial_data["dev_mode"]; + } else { + $this->errors[] = APIResponse\get(2106); + } + # Assume default if none was specified + } else { + $this->validated_data["dev_mode"] = "tun"; + } + } + + private function __validate_interface() { + if (($this->validated_data["protocol"] === "UDP") or ($this->validated_data["protocol"] === "TCP")) { + $this->validated_data["interface"] = "wan"; + $this->validated_data["ipaddr"] = ""; + } elseif (isset($this->initial_data["interface"])) { + # Local variables + $match = false; + $interface = $this->initial_data["interface"]; + + // Validate Interface + if (!empty(APITools\get_pfsense_if_id($interface))) { + if (isset($this->config["interfaces"][APITools\get_pfsense_if_id($interface)]["enable"])) { + $this->validated_data["interface"] = APITools\get_pfsense_if_id($interface); + $this->validated_data["ipaddr"] = ""; + $match = true; + } + } + + // Validate Virtual Interface + if (!$match) { + foreach ($this->config["virtualip"]["vip"] as $vip) { + if (($vip["mode"] === "ipalias") or ($vip["mode"] === "carp") ) { + if (strtolower($this->initial_data["interface"]) === strtolower($vip["descr"])) { + $this->validated_data["interface"] = "_vip" . $vip["uniqid"]; + $this->validated_data["ipaddr"] = $vip["subnet"]; + $match = true; + break; + } elseif ((filter_var($vip["subnet"], FILTER_VALIDATE_IP)) and ($this->initial_data["interface"] === $vip["subnet"])) { + $this->validated_data["interface"] = "_vip" . $vip["uniqid"]; + $this->validated_data["ipaddr"] = $vip["subnet"]; + $match = true; + break; + } elseif (strtolower($this->initial_data["interface"]) === $vip["uniqid"]) { + $this->validated_data["interface"] = "_vip" . $vip["uniqid"]; + $this->validated_data["ipaddr"] = $vip["subnet"]; + $match = true; + break; + } + } + } + } + + // Validate Gateway Group Interface + if (!$match) { + foreach ($this->config["gateways"]["gateway_group"] as $gateway_group) { + if (strtolower($this->initial_data["interface"]) === strtolower($gateway_group['name'])) { + $this->validated_data["interface"] = $gateway_group['name']; + $this->validated_data["ipaddr"] = ""; + $match = true; + break; + } + } + } + + if (!$match) { + $this->errors[] = APIResponse\get(2107); + } + # Assume default if none was specified + } else { + $this->validated_data["interface"] = "wan"; + $this->validated_data["ipaddr"] = ""; + } + } + + private function __validate_local_port() { + # Only create local_port if no errors were found + if ((empty($this->errors)) and (isset($this->initial_data["local_port"]))) { + if (!openvpn_validate_port($this->initial_data["local_port"], "local_port", 1)) { + if (openvpn_port_used($this->validated_data["protocol"], $this->validated_data["interface"], $this->initial_data["local_port"], 0) == 0) { + $this->validated_data["local_port"] = $this->initial_data["local_port"]; + } else { + $this->errors[] = APIResponse\get(2108); + } + } else { + $this->errors[] = APIResponse\get(2108); + } + # Assume default if none was specified + } else { + $this->validated_data["local_port"] = (string) openvpn_port_next($this->validated_data["protocol"], $this->validated_data["interface"]); + } + } + + private function __validate_description() { + # Check for our optional `descr` payload value + if (isset($this->initial_data['description'])) { + $this->validated_data["description"] = $this->initial_data['description']; + } + # Assume default if none was specified + else { + $this->validated_data["description"] = ""; + } + } + + private function __validate_custom_options() { + # Check for our optional `Custom options` payload value + if (isset($this->initial_data['custom_options'])) { + $this->validated_data["custom_options"] = $this->initial_data['custom_options']; + # Assume default if none was specified + } else { + $this->validated_data["custom_options"] = ""; + } + } + + private function __validate_tls() { + # Check for our optional `TLS` payload value + if (isset($this->initial_data["tls"])) { + # Auto generate TLS key if empty + if ($this->initial_data["tls"] == "" or $this->initial_data["tls"] == "true") { + $this->validated_data["tls"] = base64_encode(openvpn_create_key()); + } else { + $this->validated_data["tls"] = base64_encode($this->initial_data["tls"]); + } + } + } + + private function __validate_tls_type() { + # Local variables + $key_types = ["auth", "crypt"]; + + # Validate TLS type if TLS set + if (($this->initial_data["tls"] !== "") and (isset($this->validated_data['tls']))) { + if (isset($this->initial_data["tls_type"])) { + if (in_array($this->initial_data["tls_type"], $key_types)) { + $this->validated_data["tls_type"] = $this->initial_data["tls_type"]; + } else { + $this->errors[] = APIResponse\get(2109); + } + # Assume default if none was specified + } else { + $this->validated_data["tls_type"] = "auth"; + } + } + } + + private function __validate_tls_keydir() { + # Local variables + $key_types = ["default", "direction-0", "direction-1", "both-directions"]; + + # Validate keydir direction if TLS set + if (($this->initial_data["tls"] !== "") and (isset($this->validated_data['tls']))) { + if (isset($this->initial_data["tlsauth_keydir"])) { + if (in_array($this->initial_data["tlsauth_keydir"], $key_types)) { + switch ($this->initial_data["tlsauth_keydir"]) { + case "default": + $this->validated_data["tlsauth_keydir"] = "default"; + break; + case "direction-0": + $this->validated_data["tlsauth_keydir"] = "0"; + break; + case "direction-1": + $this->validated_data["tlsauth_keydir"] = "1"; + break; + case "both-directions": + $this->validated_data["tlsauth_keydir"] = "2"; + break; + } + } else { + $this->errors[] = APIResponse\get(2110); + } + # Assume default if none was specified + } else { + $this->validated_data["tlsauth_keydir"] = "default"; + } + } + } + + private function __validate_shared_key() { + # Check for our optional `Shared key` payload value + if (isset($this->initial_data["shared_key"])) { + # Auto generate Shared key if empty + if ($this->initial_data["shared_key"] === "") { + $this->validated_data["shared_key"] = base64_encode(openvpn_create_key()); + # Assume default if none was specified + } else { + $this->validated_data["shared_key"] = base64_encode($this->initial_data["shared_key"]); + } + } else { + $this->errors[] = APIResponse\get(2141); + } + } + + private function __validate_caref() { + if (!empty($this->config["ca"])) { + if (isset($this->initial_data["caref"])) { + foreach ($this->config["ca"] as $ca) { + if ($this->initial_data["caref"] === $ca["refid"]) { + $this->validated_data["caref"] = $ca["refid"]; + break; + } + } + + if (empty($this->validated_data["caref"])) { + $this->errors[] = APIResponse\get(2142); + } + } else { + $this->errors[] = APIResponse\get(2136); + } + } else { + $this->errors[] = APIResponse\get(2144); + } + } + + private function __validate_crlref() { + if (isset($this->initial_data["crlref"])) { + foreach ($this->config["crl"] as $crl) { + if (($this->validated_data["caref"] === $crl["caref"]) and ($this->initial_data["crlref"] === $crl["refid"])) { + $this->validated_data["crlref"] = $this->initial_data["refid"]; + break; + } + } + + if ($this->initial_data["crlref"] === "none") { + $this->validated_data["crlref"] = ""; + } + + if (empty($this->validated_data["crlref"])) { + $this->errors[] = APIResponse\get(2137); + } + + } else { + $this->validated_data["crlref"] = ""; + } + } + + private function __validate_ocspcheck() { + # Check for our optional `OCSP` payload value + if ($this->initial_data["ocspcheck"] === true) { + $this->validated_data["ocspcheck"] = "yes"; + } + } + + private function __validate_ocspurl() { + # Check for our optional `OCSP URL` payload value + if ($this->validated_data["ocspcheck"] === "yes") { + if (isset($this->initial_data["ocspurl"])) { + $this->validated_data["ocspurl"] = $this->initial_data["ocspurl"]; + } else { + $this->validated_data["ocspurl"] = ""; + } + # Assume default if none was specified + } else { + $this->validated_data["ocspurl"] = ""; + } + } + + private function __validate_certref() { + if (!empty($this->config["cert"])) { + if (isset($this->initial_data["certref"])) { + foreach ($this->config["cert"] as $cert) { + if ($this->initial_data["certref"] === $cert["refid"]) { + $this->validated_data["certref"] = $cert["refid"]; + break; + } + } + } else { + $this->errors[] = APIResponse\get(2135); + } + + if (empty($this->validated_data["certref"])) { + $this->errors[] = APIResponse\get(2143); + } + } else { + $this->errors[] = APIResponse\get(2145); + } + } + + private function __validate_dh_length() { + # Local variables + $key_types = ["1024", "2048", "3072", "4096", "6144", "7680", "8192", "15360", "16384", "ECDH Only"]; + + # Validate DH Parameter Length + if (isset($this->initial_data["dh_length"])) { + if (in_array($this->initial_data["dh_length"], $key_types)) { + if ($this->initial_data["dh_length"] === "ECDH Only") { + $this->validated_data["dh_length"] = "none"; + } else { + $this->validated_data["dh_length"] = $this->initial_data["dh_length"]; + } + } else { + $this->errors[] = APIResponse\get(2111); + } + # Assume default if none was specified + } else { + $this->validated_data["dh_length"] = "2048"; + } + } + + private function __validate_ecdh_curve() { + # Local variables + $key_types = ["none", "prime256v1", "secp384r1", "secp521r1"]; + + # Validate ECDH Curve + if (isset($this->initial_data["ecdh_curve"])) { + if (in_array($this->initial_data["ecdh_curve"], $key_types)) { + $this->validated_data["ecdh_curve"] = $this->initial_data["ecdh_curve"]; + } else { + $this->errors[] = APIResponse\get(2112); + } + # Assume default if none was specified + } else { + $this->validated_data["ecdh_curve"] = "none"; + } + } + + private function __validate_cert_depth() { + # Local variables + $key_types = ["none", "one", "two", "three", "four", "five"]; + + # Validate Certificate Depth + if (isset($this->initial_data["cert_depth"])) { + if (in_array($this->initial_data["cert_depth"], $key_types)) { + switch ($this->initial_data["cert_depth"]) { + case "none": + $this->validated_data["cert_depth"] = ""; + break; + case "one": + $this->validated_data["cert_depth"] = "1"; + break; + case "two": + $this->validated_data["cert_depth"] = "2"; + break; + case "three": + $this->validated_data["cert_depth"] = "3"; + break; + case "four": + $this->validated_data["cert_depth"] = "4"; + break; + case "five": + $this->validated_data["cert_depth"] = "5"; + break; + } + } else { + $this->errors[] = APIResponse\get(2113); + } + # Assume default if none was specified + } else { + $this->validated_data["cert_depth"] = "1"; + } + } + + private function __validate_strick_user_cn() { + if ($this->initial_data['strictusercn'] === true) { + $this->validated_data["strictusercn"] = "yes"; + # Assume default if none was specified + } else { + $this->validated_data["strictusercn"] = ""; + } + } + + private function __validate_ncp_enable() { + if ($this->initial_data["data_encryption_negotiation"] === false) { + $this->validated_data["ncp_enable"] = "disabled"; + } else { + $this->validated_data["ncp_enable"] = "enabled"; + } + } + + private function __validate_data_ciphers() { + if (isset($this->initial_data["data_ciphers"])) { + $data_ciphers = explode(',', $this->initial_data['data_ciphers']); + $counter = 1; + foreach ($data_ciphers as $cipher) { + if (!array_key_exists($cipher, openvpn_get_cipherlist())) { + $this->errors[] = APIResponse\get(2138); + } elseif ($counter > 19) { + break; + } else { + $counter += 1; + } + } + $this->validated_data['data_ciphers'] = $this->initial_data['data_ciphers']; + } else { + $this->validated_data["data_ciphers"] = "AES-256-GCM,AES-128-GCM,CHACHA20-POLY1305"; + } + } + + private function __validate_data_ciphers_fallback() { + # Validate Fallback Data Encryption Algorithm + if (isset($this->initial_data["data_ciphers_fallback"])) { + if (array_key_exists($this->initial_data["data_ciphers_fallback"], openvpn_get_cipherlist())) { + $this->validated_data["data_ciphers_fallback"] = $this->initial_data["data_ciphers_fallback"]; + } else { + $this->errors[] = APIResponse\get(2114); + } + # Assume default if none was specified + } else { + $this->validated_data["data_ciphers_fallback"] = "AES-256-CBC"; + } + } + + private function __validate_digest() { + # Local variables + $key_types = ["SHA256", "BLAKE2b512", "BLAKE2b256", "MD4", "MD5", "MD5-SHA1", "MDC2", "RIPEMD160", "SHA1", "SHA224", "SHA256", "SHA3-224", "SHA3-256", "SHA3-384", "SHA3-512", "SHA384", "SHA512", "SHA512-224", "SHA512-256", "SHAKE128", "SHAKE256", "SM3", "whirlpool", "none"]; + + # Validate Auth digest algorithm + if (isset($this->initial_data["digest"])) { + if (in_array($this->initial_data["digest"], $key_types)) { + $this->validated_data["digest"] = $this->initial_data["digest"]; + } else { + $this->errors[] = APIResponse\get(2115); + } + # Assume default if none was specified + } else { + $this->validated_data["digest"] = "SHA256"; + } + } + + private function __validate_engine() { + # Validate Hardware Crypto + if (isset($this->initial_data["engine"])) { + if (array_key_exists($this->initial_data["engine"], openvpn_get_engines())) { + $this->validated_data["engine"] = $this->initial_data["engine"]; + } else { + $this->errors[] = APIResponse\get(2116); + } + # Assume default if none was specified + } else { + $this->validated_data["engine"] = "none"; + } + } + + private function __validate_tunnel_network() { + # Validate Tunnel Network + if (isset($this->initial_data['tunnel_network'])) { + if (!openvpn_validate_cidr($this->initial_data['tunnel_network'], "", false, "ipv4")) { + if (!openvpn_is_tunnel_network_in_use($this->initial_data['tunnel_network'])) { + $this->validated_data["tunnel_network"] = $this->initial_data['tunnel_network']; + } else { + $this->errors[] = APIResponse\get(2117); + } + } else { + $this->errors[] = APIResponse\get(2117); + } + # Assume default if none was specified + } else { + $this->validated_data["tunnel_network"] = ""; + } + } + + private function __validate_tunnel_networkv6() { + # Validate Tunnel Network v6 + if (isset($this->initial_data['tunnel_networkv6'])) { + if (!openvpn_validate_cidr($this->initial_data['tunnel_networkv6'], "", false, "ipv6")) { + if (!openvpn_is_tunnel_network_in_use($this->initial_data['tunnel_networkv6'])) { + $this->validated_data["tunnel_networkv6"] = $this->initial_data['tunnel_networkv6']; + } else { + $this->errors[] = APIResponse\get(2117); + } + } else { + $this->errors[] = APIResponse\get(2117); + } + # Assume default if none was specified + } else { + $this->validated_data["tunnel_networkv6"] = ""; + } + } + + private function __validate_serverbridge_dhcp() { + if ($this->validated_data['dev_mode'] === 'tap') { + if ($this->initial_data['serverbridge_dhcp'] === true) { + $this->validated_data['serverbridge_dhcp'] = 'yes'; + } else { + $this->validated_data['serverbridge_dhcp'] = ""; + } + } else { + $this->validated_data['serverbridge_dhcp'] = ""; + } + } + + private function __validate_serverbridge_interface() { + $key_types = ["wan", "lan", "none"]; + $match = false; + + if (($this->validated_data['serverbridge_dhcp'] === 'yes') and (isset($this->initial_data['serverbridge_interface']))) { + // Validate Virtual Interface + foreach ($this->config["virtualip"]["vip"] as $vip) { + if (strtolower($this->initial_data["serverbridge_interface"]) === strtolower($vip["descr"])) { + $this->validated_data["interface"] = "_vip" . $vip["uniqid"] . "|" . $vip["subnet"]; + $match = true; + break; + } elseif ((filter_var($vip["subnet"], FILTER_VALIDATE_IP)) and ($this->initial_data["serverbridge_interface"] === $vip["subnet"])) { + $this->validated_data["interface"] = "_vip" . $vip["uniqid"] . "|" . $vip["subnet"]; + $match = true; + break; + } elseif (strtolower($this->initial_data["serverbridge_interface"]) === strtolower($vip["uniqid"])) { + $this->validated_data["interface"] = "_vip" . $vip["uniqid"] . "|" . $vip["subnet"]; + $match = true; + break; + } + } + + if (in_array(strtolower($this->initial_data['serverbridge_interface']), $key_types)) { + $this->validated_data["serverbridge_interface"] = strtolower($this->initial_data['serverbridge_interface']); + $match = true; + } + + if (!$match) { + $this->errors[] = APIResponse\get(2146); + } + + } else { + $this->validated_data["serverbridge_interface"] = "none"; + } + } + + private function __validate_serverbridge_routegateway() { + if ($this->validated_data['serverbridge_dhcp'] === 'yes') { + if ($this->initial_data['serverbridge_routegateway'] === true) { + if ($this->validated_data["serverbridge_interface"] !== "none") { + $this->validated_data["serverbridge_routegateway"] = "yes"; + } else { + $this->errors[] = APIResponse\get(2147); + } + } else { + $this->validated_data["serverbridge_routegateway"] = ""; + } + } else { + $this->validated_data["serverbridge_routegateway"] = ""; + } + } + + private function __validate_serverbridge_dhcp_start() { + if ($this->validated_data['serverbridge_dhcp'] === 'yes') { + if ((isset($this->initial_data['serverbridge_dhcp_start'])) and (is_ipaddrv4($this->initial_data['serverbridge_dhcp_start'])) and (isset($this->initial_data['serverbridge_dhcp_end']))) { + $this->validated_data["serverbridge_dhcp_start"] = $this->initial_data['serverbridge_dhcp_start']; + } else { + $this->errors[] = APIResponse\get(2148); + } + } else { + $this->validated_data["serverbridge_dhcp_start"] = ""; + } + } + + private function __validate_serverbridge_dhcp_end() { + if ($this->validated_data['serverbridge_dhcp'] === 'yes') { + if ((isset($this->initial_data['serverbridge_dhcp_end'])) and (is_ipaddrv4($this->initial_data['serverbridge_dhcp_end'])) and (isset($this->initial_data['serverbridge_dhcp_start']))) { + $this->validated_data["serverbridge_dhcp_end"] = $this->initial_data['serverbridge_dhcp_end']; + } else { + $this->errors[] = APIResponse\get(2148); + } + } else { + $this->validated_data["serverbridge_dhcp_end"] = ""; + } + } + + private function __validate_remote_network() { + # Check for our optional `Remote Network` payload value + if (isset($this->initial_data['remote_network'])) { + if (!openvpn_validate_cidr($this->initial_data['remote_network'], "", true, "ipv4")) { + $this->validated_data["remote_network"] = $this->initial_data['remote_network']; + } else { + $this->errors[] = APIResponse\get(2118); + } + # Assume default if none was specified + } else { + $this->validated_data["remote_network"] = ""; + } + } + + private function __validate_remote_networkv6() { + # Check for our optional `Remote Network v6` payload value + if (isset($this->initial_data['remote_networkv6'])) { + if (!openvpn_validate_cidr($this->initial_data['remote_networkv6'], "", true, "ipv6")) { + $this->validated_data["remote_networkv6"] = $this->initial_data['remote_networkv6']; + } else { + $this->errors[] = APIResponse\get(2118); + } + # Assume default if none was specified + } else { + $this->validated_data["remote_networkv6"] = ""; + } + } + + private function __validate_redirect_gateway() { + if ($this->initial_data['redirect_gateway'] === true) { + $this->validated_data["gwredir"] = "yes"; + # Assume default if none was specified + } else { + $this->validated_data["gwredir"] = ""; + } + } + + private function __validate_redirect_gateway6() { + if ($this->initial_data['redirect_gateway6'] === true) { + $this->validated_data["gwredir6"] = "yes"; + # Assume default if none was specified + } else { + $this->validated_data["gwredir6"] = ""; + } + } + + private function __validate_local_network() { + if ($this->validated_data['gwredir'] === "") { + if (isset($this->initial_data['local_network'])) { + if (!openvpn_validate_cidr($this->initial_data['local_network'], "", true, "ipv4")) { + $this->validated_data["local_network"] = $this->initial_data['local_network']; + } else { + $this->errors[] = APIResponse\get(2119); + } + # Assume default if none was specified + } else { + $this->validated_data["local_network"] = ""; + } + # Assume default if none was specified + } else { + $this->validated_data["local_network"] = ""; + } + } + + private function __validate_local_networkv6() { + if ($this->validated_data['gwredir6'] === "") { + if (isset($this->initial_data['local_networkv6'])) { + if (!openvpn_validate_cidr($this->initial_data['local_networkv6'], "", true, "ipv6")) { + $this->validated_data["local_networkv6"] = $this->initial_data['local_networkv6']; + } else { + $this->errors[] = APIResponse\get(2119); + } + # Assume default if none was specified + } else { + $this->validated_data["local_networkv6"] = ""; + } + # Assume default if none was specified + } else { + $this->validated_data["local_networkv6"] = ""; + } + } + + private function __validate_concurrent_connections() { + if (isset($this->initial_data['concurrent_connections'])) { + if (is_numericint($this->initial_data['concurrent_connections']) and (int) $this->initial_data['concurrent_connections'] > 0) { + $this->validated_data["maxclients"] = $this->initial_data['concurrent_connections']; + } else { + $this->errors[] = APIResponse\get(2133); + } + # Assume default if none was specified + } else { + $this->validated_data["maxclients"] = ""; + } + } + + private function __validate_allow_compression() { + # Local variables + $key_types = ["asym", "no", "yes"]; + + # Validate Allow Compression + if (isset($this->initial_data["allow_compression"])) { + if (in_array($this->initial_data["allow_compression"], $key_types)) { + $this->validated_data["allow_compression"] = $this->initial_data["allow_compression"]; + } else { + $this->errors[] = APIResponse\get(2120); + } + # Assume default if none was specified + } else { + $this->validated_data["allow_compression"] = "no"; + } + } + + private function __validate_compression() { + # Local variables + $key_types = ["none", "stub", "stub-v2", "lz4", "lz4-v2", "lzo", "noadapt", "adaptive", "yes", "no"]; + + if (($this->validated_data["allow_compression"] === "yes") or ($this->validated_data["allow_compression"] === "asym")) { + # Validate Compression + if (isset($this->initial_data["compression"])) { + if (in_array($this->initial_data["compression"], $key_types)) { + $this->validated_data["compression"] = $this->initial_data["compression"]; + } else { + $this->errors[] = APIResponse\get(2121); + } + # Assume default if none was specified + } else { + $this->validated_data["compression"] = ""; + } + # Assume default if none was specified + } else { + $this->validated_data["compression"] = ""; + } + } + + private function __validate_compression_push() { + if ($this->initial_data['compression_push'] === true) { + $this->validated_data["compression_push"] = "yes"; + # Assume default if none was specified + } else { + $this->validated_data["compression_push"] = ""; + } + } + + private function __validate_type_of_service() { + if ($this->initial_data['passtos'] === true) { + $this->validated_data["passtos"] = "yes"; + # Assume default if none was specified + } else { + $this->validated_data["passtos"] = ""; + } + } + + private function __validate_inter_client_communication() { + if ($this->initial_data['client2client'] === true) { + $this->validated_data["client2client"] = "yes"; + # Assume default if none was specified + } else { + $this->validated_data["client2client"] = ""; + } + } + + private function __validate_duplicate_connection() { + if ($this->initial_data['duplicate_cn'] === true) { + $this->validated_data["duplicate_cn"] = ""; + } + } + + private function __validate_dynamic_ip() { + if ($this->initial_data['dynamic_ip'] === true) { + $this->validated_data["dynamic_ip"] = "yes"; + } + # Assume default if none was specified + else { + $this->validated_data["dynamic_ip"] = ""; + } + } + + private function __validate_topology() { + # Local variables + $key_types = ["subnet", "net30"]; + + if ($this->validated_data["dev_mode"] === "tap") { + $this->validated_data["topology"] = "subnet"; + } elseif (isset($this->initial_data["topology"])) { + if (in_array($this->initial_data["topology"], $key_types)) { + $this->validated_data["topology"] = $this->initial_data["topology"]; + } else { + $this->errors[] = APIResponse\get(2122); + } + # Assume default if none was specified + } else { + $this->validated_data["topology"] = "subnet"; + } + } + + private function __validate_dns_domain() { + if (isset($this->initial_data['dns_domain'])) { + $this->validated_data["dns_domain"] = $this->initial_data['dns_domain']; + } + else { + $this->validated_data["dns_domain"] = ""; + } + } + + private function __validate_dns_servers() { + if (isset($this->initial_data['dns_servers'])) { + $dns_servers = explode(',', $this->initial_data['dns_servers']); + $counter = 1; + foreach ($dns_servers as $server) { + if ($counter > 4) { + break; + } elseif (is_ipaddrv4(str_replace(" ", "", $server))) { + $this->validated_data["dns_server" . $counter] = str_replace(" ", "", $server); + $counter += 1; + } else { + $this->errors[] = APIResponse\get(2150); + } + } + } + } + + private function __validate_push_blockout_side_dns() { + if ($this->initial_data['push_blockoutsidedns'] === true) { + $this->validated_data["push_blockoutsidedns"] = "yes"; + } + } + + private function __validate_inactive_seconds() { + if (isset($this->initial_data['inactive_seconds'])) { + if (is_numericint($this->initial_data['inactive_seconds']) and ((int) $this->initial_data['inactive_seconds']) >= 0) { + $this->validated_data["inactive_seconds"] = $this->initial_data['inactive_seconds']; + # Assume default if none was specified + } else { + $this->errors[] = APIResponse\get(2134); + } + # Assume default if none was specified + } else { + $this->validated_data["inactive_seconds"] = "300"; + } + } + + private function __validate_ping_method() { + # Local variables + $key_types = ["keepalive", "ping"]; + + # Validate Ping method + if (isset($this->initial_data["ping_method"])) { + if (in_array($this->initial_data["ping_method"], $key_types)) { + $this->validated_data["ping_method"] = $this->initial_data["ping_method"]; + } else { + $this->errors[] = APIResponse\get(2123); + } + # Assume default if none was specified + } else { + $this->validated_data["ping_method"] = "keepalive"; + } + } + + private function __validate_keepalive_interval() { + if ($this->validated_data["ping_method"] === "keepalive") { + if (isset($this->initial_data['keepalive_interval'])) { + if (is_numericint($this->initial_data['keepalive_interval']) and ((int) $this->initial_data['keepalive_interval'] >= 0)) { + $this->validated_data["keepalive_interval"] = $this->initial_data['keepalive_interval']; + } else { + $this->errors[] = APIResponse\get(2124); + } + # Assume default if none was specified + } else { + $this->validated_data["keepalive_interval"] = "10"; + } + # Assume default if none was specified + } else { + $this->validated_data["keepalive_interval"] = "10"; + } + } + + private function __validate_keepalive_timeout() { + if ($this->validated_data["ping_method"] === "keepalive") { + if (isset($this->initial_data['keepalive_timeout'])) { + if (is_numericint($this->initial_data['keepalive_timeout']) and ((int) $this->initial_data['keepalive_timeout'] >= 0)) { + $this->validated_data["keepalive_timeout"] = $this->initial_data['keepalive_timeout']; + } else { + $this->errors[] = APIResponse\get(2125); + } + # Assume default if none was specified + } else { + $this->validated_data["keepalive_timeout"] = "60"; + } + # Assume default if none was specified + } else { + $this->validated_data["keepalive_timeout"] = "60"; + } + } + + private function __validate_ping_seconds() { + if ($this->validated_data["ping_method"] === "ping") { + if (isset($this->initial_data['ping_seconds'])) { + if (is_numericint($this->initial_data['ping_seconds']) and ((int) $this->initial_data['ping_seconds'] >= 0)) { + $this->validated_data["ping_seconds"] = $this->initial_data['ping_seconds']; + } else { + $this->errors[] = APIResponse\get(2126); + } + # Assume default if none was specified + } else { + $this->validated_data["ping_seconds"] = "10"; + } + # Assume default if none was specified + } else { + $this->validated_data["ping_seconds"] = "10"; + } + } + + private function __validate_ping_action_seconds() { + if ($this->validated_data["ping_method"] === "ping") { + if (isset($this->initial_data['ping_action_seconds'])) { + if (is_numericint($this->initial_data['ping_action_seconds']) and ((int) $this->initial_data['ping_action_seconds'] >= 0)) { + $this->validated_data["ping_action_seconds"] = $this->initial_data['ping_action_seconds']; + } else { + $this->errors[] = APIResponse\get(2127); + } + # Assume default if none was specified + } else { + $this->validated_data["ping_action_seconds"] = "60"; + } + # Assume default if none was specified + } else { + $this->validated_data["ping_action_seconds"] = "60"; + } + } + + private function __validate_ping_push() { + if ($this->validated_data["ping_method"] === "ping") { + if ($this->initial_data['ping_push'] === true) { + $this->validated_data["ping_push"] = "yes"; + # Assume default if none was specified + } else { + $this->validated_data["ping_push"] = ""; + } + # Assume default if none was specified + } else { + $this->validated_data["ping_push"] = ""; + } + } + + private function __validate_ping_action_push() { + if ($this->validated_data["ping_method"] === "ping") { + if ($this->initial_data['ping_action_push'] === true) { + $this->validated_data["ping_action_push"] = "yes"; + } + # Assume default if none was specified + else { + $this->validated_data["ping_action_push"] = ""; + } + # Assume default if none was specified + } else { + $this->validated_data["ping_action_push"] = ""; + } + } + + private function __validate_ping_action() { + # Local variables + $key_types = ["ping_exit", "ping_restart"]; + + # Validate Ping restart or exit + if (($this->validated_data["ping_method"] === "ping") and (isset($this->initial_data["ping_action"]))) { + if (in_array($this->initial_data["ping_action"], $key_types)) { + $this->validated_data["ping_action"] = $this->initial_data["ping_action"]; + } + else { + $this->errors[] = APIResponse\get(2128); + } + # Assume default if none was specified + } else { + $this->validated_data["ping_action"] = "ping_restart"; + } + } + + private function __validate_username_as_cn() { + if ($this->initial_data['username_as_common_name'] === true) { + $this->validated_data["username_as_common_name"] = "enabled"; + } else { + $this->validated_data["username_as_common_name"] = "disabled"; + } + } + + private function __validate_udp_fast_io() { + if ($this->initial_data['udp_fast_io'] === true) { + $this->validated_data["udp_fast_io"] = "yes"; + } + } + + private function __validate_exit_notify() { + # Local variables + $key_types = ["once", "twice", "none", ""]; + + # Validate Exit Notify + if (isset($this->initial_data["exit_notify"])) { + if (in_array($this->initial_data["exit_notify"], $key_types)) { + switch ($this->initial_data["exit_notify"]) { + case "none": + $this->validated_data["exit_notify"] = "none"; + break; + case "": + $this->validated_data["exit_notify"] = "none"; + break; + case "once": + $this->validated_data["exit_notify"] = "1"; + break; + case "twice": + $this->validated_data["exit_notify"] = "2"; + break; + } + } else { + $this->errors[] = APIResponse\get(2129); + } + # Assume default if none was specified + } else { + $this->validated_data["exit_notify"] = "1"; + } + } + + private function __validate_snd_rcv_buf() { + # Local variables + $key_types = ["64KiB", "128KiB", "256KiB", "512KiB", "1MiB", "2MiB", "default"]; + + # Validate Send/Receive Buffer + if (isset($this->initial_data["sndrcvbuf"])) { + if (in_array($this->initial_data["sndrcvbuf"], $key_types)) { + switch ($this->initial_data["sndrcvbuf"]) { + case "64KiB": + $this->validated_data["sndrcvbuf"] = "65536"; + break; + case "128KiB": + $this->validated_data["sndrcvbuf"] = "131072"; + break; + case "256KiB": + $this->validated_data["sndrcvbuf"] = "262144"; + break; + case "512KiB": + $this->validated_data["sndrcvbuf"] = "524288"; + break; + case "1MiB": + $this->validated_data["sndrcvbuf"] = "1048576"; + break; + case "2MiB": + $this->validated_data["sndrcvbuf"] = "2097152"; + break; + case "default": + $this->validated_data["sndrcvbuf"] = ""; + break; + } + } else { + $this->errors[] = APIResponse\get(2130); + } + } else { + $this->validated_data["sndrcvbuf"] = ""; + } + } + + private function __validate_push_register_dns() { + if ($this->initial_data['push_register_dns'] === true) { + $this->validated_data["push_register_dns"] = "yes"; + } + } + + private function __validate_ntp_servers() { + if (isset($this->initial_data['ntp_servers'])) { + $ntp_servers = explode(',', $this->initial_data['ntp_servers']); + $counter = 1; + foreach ($ntp_servers as $server) { + if ($counter > 2) { + break; + } elseif (is_ipaddrv4(str_replace(" ", "", $server))) { + $this->validated_data["ntp_server" . $counter] = str_replace(" ", "", $server); + $counter += 1; + } else { + $this->errors[] = APIResponse\get(2149); + } + } + } + } + + private function __validate_netbios_enable() { + if ($this->initial_data['netbios_enable'] === true) { + $this->validated_data["netbios_enable"] = "yes"; + # Assume default if none was specified + } else { + $this->validated_data["netbios_enable"] = ""; + } + } + + private function __validate_netbios_ntype() { + if (isset($this->initial_data['netbios_node_type'])) { + if ($this->initial_data['netbios_node_type'] == 'b') { + $this->validated_data["netbios_ntype"] = "1"; + } else if ($this->initial_data['netbios_node_type'] == 'p') { + $this->validated_data["netbios_ntype"] = "2"; + } else if ($this->initial_data['netbios_node_type'] == 'm') { + $this->validated_data["netbios_ntype"] = "3"; + } else if ($this->initial_data['netbios_node_type'] == 'h') { + $this->validated_data["netbios_ntype"] = "4"; + } else { + $this->errors[] = APIResponse\get(2076); + } + } else { + if (isset($this->initial_data['netbios_enable'])) { + $this->validated_data["netbios_ntype"] = "1"; + } else { + $this->validated_data["netbios_ntype"] = "0"; + } + } + } + + private function __validate_netbios_scope() { + if (isset($this->initial_data['netbios_scope'])) { + $this->validated_data["netbios_scope"] = $this->initial_data['netbios_scope']; + } else { + $this->validated_data["netbios_scope"] = ""; + } + } + + private function __validate_create_gw() { + # Local variables + $key_types = ["both", "v4only", "v6only"]; + + # Validate Gateway creation + if (isset($this->initial_data["create_gw"])) { + if (in_array(strtolower($this->initial_data["create_gw"]), $key_types)) { + switch ($this->initial_data["create_gw"]) { + case "both": + $this->validated_data["create_gw"] = strtolower($this->initial_data["create_gw"]); + break; + case "v4only": + $this->validated_data["create_gw"] = strtolower($this->initial_data["create_gw"]); + break; + case "v6only": + $this->validated_data["create_gw"] = strtolower($this->initial_data["create_gw"]); + break; + } + } else { + $this->errors[] = APIResponse\get(2131); + } + # Assume default if none was specified + } else { + $this->validated_data["create_gw"] = "both"; + } + } + + private function __validate_verbosity_level() { + if (isset($this->initial_data['verbosity_level'])) { + if (is_numericint($this->initial_data['verbosity_level']) and ((int) $this->initial_data['verbosity_level'] >= 2) and ((int) $this->initial_data['verbosity_level'] < 13)) { + $this->validated_data["verbosity_level"] = $this->initial_data["verbosity_level"]; + } elseif ($this->initial_data['verbosity_level'] == "none") { + $this->validated_data["verbosity_level"] = "0"; + } elseif ($this->initial_data['verbosity_level'] == "default") { + $this->validated_data["verbosity_level"] = "1"; + } else { + $this->errors[] = APIResponse\get(2132); + } + # Assume default if none was specified + } else { + $this->validated_data["verbosity_level"] = "1"; + } + } + + private function __validate_wins_servers() { + if (isset($this->initial_data['wins_servers'])) { + $win_servers = explode(',', $this->initial_data['wins_servers']); + $counter = 1; + foreach ($win_servers as $server) { + if ($counter > 2) { + break; + } elseif (is_ipaddrv4(str_replace(" ", "", $server))) { + $this->validated_data["wins_server" . $counter] = str_replace(" ", "", $server); + $counter += 1; + } else { + $this->errors[] = APIResponse\get(2151); + } + } + } + } + + // TODO: Validate nbdd servers is not yet available + private function __validate_nbdd_server() { + if (isset($this->initial_data['wins_servers']) and ($this->validated_data["netbios_enable"] = "yes")) { + $this->validated_data["nbdd_server1"] = ""; + } + } + + private function __validate_disabled() { + # Check for our optional `disabled` payload value + if ($this->initial_data["disable"] === true) { + $this->validated_data["disable"] = ""; + } + } + + public function validate_payload() { + # Run each validation method + $this->__validate_vpnid(); + $this->__validate_mode(); + $this->__validate_description(); + $this->__validate_disabled(); + $this->__validate_dev_mode(); + $this->__validate_protocol(); + $this->__validate_interface(); + $this->__validate_local_port(); + $this->__validate_ncp_enable(); + $this->__validate_data_ciphers(); + $this->__validate_data_ciphers_fallback(); + $this->__validate_engine(); + $this->__validate_digest(); + $this->__validate_tunnel_network(); + $this->__validate_tunnel_networkv6(); + $this->__validate_concurrent_connections(); + $this->__validate_allow_compression(); + $this->__validate_compression(); + $this->__validate_type_of_service(); + $this->__validate_ping_method(); + $this->__validate_keepalive_interval(); + $this->__validate_keepalive_timeout(); + $this->__validate_ping_seconds(); + $this->__validate_ping_action_seconds(); + $this->__validate_ping_push(); + $this->__validate_ping_action_push(); + $this->__validate_ping_action(); + $this->__validate_custom_options(); + $this->__validate_udp_fast_io(); + $this->__validate_snd_rcv_buf(); + $this->__validate_create_gw(); + $this->__validate_verbosity_level(); + + # Run each validation method for ["p2p_shared_key"] + if ($this->validated_data["mode"] === "p2p_shared_key") { + $this->__validate_shared_key(); + + $this->validated_data["gwredir"] = ""; + $this->validated_data["gwredir6"] = ""; + $this->validated_data["local_network"] = ""; + $this->validated_data["local_networkv6"] = ""; + $this->validated_data["compression_push"] = ""; + $this->validated_data["client2client"] = ""; + $this->validated_data["topology"] = "subnet"; + $this->validated_data["dynamic_ip"] = ""; + $this->validated_data["exit_notify"] = "1"; + $this->validated_data["inactive_seconds"] = "300"; + } + + # Run each validation method for ["p2p_tls", "server_tls", "server_user", "server_tls_user"] + if ($this->validated_data["mode"] !== "p2p_shared_key") { + $this->__validate_tls(); + $this->__validate_tls_type(); + $this->__validate_tls_keydir(); + $this->__validate_caref(); + $this->__validate_crlref(); + $this->__validate_ocspcheck(); + $this->__validate_ocspurl(); + $this->__validate_certref(); + $this->__validate_dh_length(); + $this->__validate_ecdh_curve(); + $this->__validate_cert_depth(); + $this->__validate_redirect_gateway(); + $this->__validate_redirect_gateway6(); + $this->__validate_local_network(); + $this->__validate_local_networkv6(); + $this->__validate_compression_push(); + $this->__validate_inter_client_communication(); + $this->__validate_duplicate_connection(); + $this->__validate_dynamic_ip(); + $this->__validate_topology(); + $this->__validate_inactive_seconds(); + $this->__validate_exit_notify(); + } + + # Run each validation method for ["p2p_tls", "p2p_shared_key"] + if (($this->validated_data["mode"] === "p2p_tls") or ($this->validated_data["mode"] === "p2p_shared_key")) { + $this->__validate_remote_network(); + $this->__validate_remote_networkv6(); + + $this->validated_data["serverbridge_dhcp"] = ""; + $this->validated_data["serverbridge_interface"] = "none"; + $this->validated_data["serverbridge_routegateway"] = ""; + $this->validated_data["serverbridge_dhcp_start"] = ""; + $this->validated_data["serverbridge_dhcp_end"] = ""; + $this->validated_data["netbios_enable"] = ""; + $this->validated_data["netbios_ntype"] = "0"; + $this->validated_data["netbios_scope"] = ""; + } + + # Run each validation method for ["server_tls", "server_user", "server_tls_user"] + if (($this->validated_data["mode"] !== "p2p_tls") and ($this->validated_data["mode"] !== "p2p_shared_key")) { + $this->__validate_dns_domain(); + $this->__validate_dns_servers(); + $this->__validate_push_blockout_side_dns(); + $this->__validate_push_register_dns(); + $this->__validate_ntp_servers(); + $this->__validate_netbios_enable(); + $this->__validate_netbios_ntype(); + $this->__validate_netbios_scope(); + $this->__validate_wins_servers(); + $this->__validate_nbdd_server(); + $this->__validate_serverbridge_dhcp(); + $this->__validate_serverbridge_interface(); + $this->__validate_serverbridge_routegateway(); + $this->__validate_serverbridge_dhcp_start(); + $this->__validate_serverbridge_dhcp_end(); + + $this->validated_data["remote_network"] = ""; + $this->validated_data["remote_networkv6"] = ""; + } + + # Run each validation method for ["p2p_tls", "p2p_shared_key", "server_tls"] + if (($this->validate_data["mode"] !== "server_user") and ($this->validated_data["mode"] !== "server_tls_user")) { + $this->validated_data["username_as_common_name"] = "disabled"; + } + + # Run each validation method for ["server_user", "server_tls_user"] + if (($this->validate_data["mode"] === "server_user") or ($this->validated_data["mode"] === "server_tls_user")) { + $this->__validate_authmode(); + $this->__validate_username_as_cn(); + + } + + # Run each validation method for ["p2p_tls"] + if ($this->validated_data["mode"] === "p2p_tls") { + } + + # Run each validation method for ["server_tls"] + if ($this->validated_data["mode"] === "server_tls") { + } + + # Run each validation method for ["server_user"] + if ($this->validate_data["mode"] === "server_user") { + } + + # Run each validation method for ["server_tls_user"] + if ($this->validated_data["mode"] === "server_tls_user") { + $this->__validate_strick_user_cn(); + } + } + + private function __init_config() { + # Ensure the OpenVPN configuration array exists + if (empty($this->config["openvpn"])) { + $this->config["openvpn"] = []; + } + + # Ensure the OpenVPN server configuration array exists + if (empty($this->config['openvpn']['openvpn-server'])) { + $this->config['openvpn']['openvpn-server'] = []; + } + } +} diff --git a/pfSense-pkg-API/files/etc/inc/api/models/APIServicesOpenVPNServerDelete.inc b/pfSense-pkg-API/files/etc/inc/api/models/APIServicesOpenVPNServerDelete.inc new file mode 100644 index 000000000..b5a4aa70b --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/models/APIServicesOpenVPNServerDelete.inc @@ -0,0 +1,67 @@ +privileges = ["page-all", "page-openvpn-server"]; + $this->change_note = "Deleted OpenVPN server via API"; + } + + public function action() { + # Remove user from backend and remove from config + openvpn_delete("server",$this->config["openvpn"]["openvpn-server"][$this->id]); + unset($this->config["openvpn"]["openvpn-server"][$this->id]); + $this->write_config(); + return APIResponse\get(0, $this->validated_data); + } + + private function __validate_vpnid() { + if (isset($this->initial_data['vpnid'])) { + $counter = 0; + foreach ($this->config["openvpn"]["openvpn-server"] as $server) { + if ((string) $this->initial_data['vpnid'] === (string) $server["vpnid"]) { + $this->id = $counter; + $this->validated_data['vpnid'] = (string) $this->initial_data['vpnid']; + break; + } + $counter += 1; + } + + if (empty($this->validated_data["vpnid"])) { + $this->errors[] = APIResponse\get(2139); + } + } else { + $this->errors[] = APIResponse\get(2140); + } + } + + public function __validate_ovpns() { + foreach ($this->config["interfaces"] as $interface) { + if (isset($interface["if"]) and ($interface["if"] == "ovpns" . $this->validated_data['vpnid'])) { + $this->errors[] = APIResponse\get(2152); + } + } + } + + public function validate_payload() { + $this->__validate_vpnid(); + $this->__validate_ovpns(); + } +} diff --git a/pfSense-pkg-API/files/etc/inc/api/models/APIServicesOpenVPNServerRead.inc b/pfSense-pkg-API/files/etc/inc/api/models/APIServicesOpenVPNServerRead.inc new file mode 100644 index 000000000..2f3b33267 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/models/APIServicesOpenVPNServerRead.inc @@ -0,0 +1,29 @@ +privileges = ["page-all", "page-openvpn-server"]; + } + + public function action() { + return APIResponse\get(0, $this->config["openvpn"]["openvpn-server"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/models/APIServicesOpenVPNServerUpdate.inc b/pfSense-pkg-API/files/etc/inc/api/models/APIServicesOpenVPNServerUpdate.inc new file mode 100644 index 000000000..06e6de50f --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/models/APIServicesOpenVPNServerUpdate.inc @@ -0,0 +1,1370 @@ +privileges = ["page-all", "page-openvpn-server"]; + $this->change_note = "Modified OpenVPN server via API"; + } + + public function action() { + # Initialize expected configuration arrays + $this->__init_config(); + + # Update server to the new configuration and resync OpenVPN to use new server + $this->config['openvpn']['openvpn-server'][$this->id] = $this->validated_data; + $this->write_config(); + + openvpn_delete('server', $this->original_server); + openvpn_resync('server', $this->validated_data); + return APIResponse\get(0, $this->validated_data); + } + + private function __validate_vpnid() { + if (isset($this->initial_data['vpnid'])) { + foreach ($this->config["openvpn"]["openvpn-server"] as $i => $server) { + if ((string) $this->initial_data['vpnid'] === (string) $server['vpnid']) { + $this->id = $i; + $this->validated_data = $this->config['openvpn']['openvpn-server'][$this->id]; + $this->original_server = $this->config['openvpn']['openvpn-server'][$this->id]; + break; + } + } + if (empty($this->validated_data["vpnid"])) { + $this->errors[] = APIResponse\get(2139); + } + } else { + $this->errors[] = APIResponse\get(2140); + } + } + + private function __validate_mode() { + # Local variables + $key_types = ["p2p_tls", "p2p_shared_key", "server_tls", "server_user", "server_tls_user"]; + + # Validate Server mode + if (isset($this->initial_data["mode"])) { + if (in_array($this->initial_data["mode"], $key_types)) { + $this->validated_data["mode"] = $this->initial_data["mode"]; + } else { + $this->errors[] = APIResponse\get(2104); + } + } + } + + private function __validate_authmode() { + # Validate Backend for authentication + if (isset($this->initial_data["authmode"])) { + $this->validated_data["authmode"] = $this->initial_data["authmode"]; + } + } + + private function __validate_protocol() { + # Local variables + $key_types = ["UDP4", "UDP6", "TCP4", "TCP6", "UDP", "TCP"]; + + # Validate Protocol + if (isset($this->initial_data["protocol"])) { + if (in_array(strtoupper($this->initial_data["protocol"]), $key_types)) { + $this->validated_data["protocol"] = strtoupper($this->initial_data["protocol"]); + } else { + $this->errors[] = APIResponse\get(2105); + } + } + } + + private function __validate_dev_mode() { + # Local variables + $key_types = ["tun", "tap"]; + + # Validate Device mode + if (isset($this->initial_data["dev_mode"])) { + if (in_array($this->initial_data["dev_mode"], $key_types)) { + $this->validated_data["dev_mode"] = $this->initial_data["dev_mode"]; + } else { + $this->errors[] = APIResponse\get(2106); + } + } + } + + private function __validate_interface() { + if (($this->validated_data["protocol"] === "UDP") or ($this->validated_data["protocol"] === "TCP")) { + $this->validated_data["interface"] = "wan"; + $this->validated_data["ipaddr"] = ""; + } elseif (isset($this->initial_data["interface"])) { + # Local variables + $match = false; + $interface = $this->initial_data["interface"]; + + // Validate Interface + if (!empty(APITools\get_pfsense_if_id($interface))) { + if (isset($this->config["interfaces"][APITools\get_pfsense_if_id($interface)]["enable"])) { + $this->validated_data["interface"] = APITools\get_pfsense_if_id($interface); + $this->validated_data["ipaddr"] = ""; + $match = true; + } + } + + // Validate Virtual Interface + if (!$match) { + foreach ($this->config["virtualip"]["vip"] as $vip) { + if (($vip["mode"] === "ipalias") or ($vip["mode"] === "carp") ) { + if (strtolower($this->initial_data["interface"]) === strtolower($vip["descr"])) { + $this->validated_data["interface"] = "_vip" . $vip["uniqid"]; + $this->validated_data["ipaddr"] = $vip["subnet"]; + $match = true; + break; + } elseif ((filter_var($vip["subnet"], FILTER_VALIDATE_IP)) and ($this->initial_data["interface"] === $vip["subnet"])) { + $this->validated_data["interface"] = "_vip" . $vip["uniqid"]; + $this->validated_data["ipaddr"] = $vip["subnet"]; + $match = true; + break; + } elseif (strtolower($this->initial_data["interface"]) === $vip["uniqid"]) { + $this->validated_data["interface"] = "_vip" . $vip["uniqid"]; + $this->validated_data["ipaddr"] = $vip["subnet"]; + $match = true; + break; + } + } + } + } + + // Validate Gateway Group Interface + if (!$match) { + foreach ($this->config["gateways"]["gateway_group"] as $gateway_group) { + if (strtolower($this->initial_data["interface"]) === strtolower($gateway_group['name'])) { + $this->validated_data["interface"] = $gateway_group['name']; + $this->validated_data["ipaddr"] = ""; + $match = true; + break; + } + } + } + + if (!$match) { + $this->errors[] = APIResponse\get(2107); + } + } + } + + private function __validate_local_port() { + # Only create local_port if no errors were found + if (isset($this->initial_data["local_port"])) { + if (!openvpn_validate_port($this->initial_data["local_port"], "local_port", 1)) { + if (openvpn_port_used($this->validated_data["protocol"], $this->validated_data["interface"], $this->initial_data["local_port"], 0) == 0) { + $this->validated_data["local_port"] = $this->initial_data["local_port"]; + } else { + $this->errors[] = APIResponse\get(2108); + } + } else { + $this->errors[] = APIResponse\get(2108); + } + } + } + + private function __validate_description() { + # Check for our optional `descr` payload value + if (isset($this->initial_data['description'])) { + $this->validated_data["description"] = $this->initial_data['description']; + } + } + + private function __validate_custom_options() { + # Check for our optional `Custom options` payload value + if (isset($this->initial_data['custom_options'])) { + $this->validated_data["custom_options"] = $this->initial_data['custom_options']; + } + } + + private function __validate_tls() { + # Check for our optional `TLS` payload value + if (isset($this->initial_data["tls"])) { + # Auto generate TLS key if empty + if ($this->initial_data["tls"] == "" or $this->initial_data["tls"] == "true") { + $this->validated_data["tls"] = base64_encode(openvpn_create_key()); + } else { + $this->validated_data["tls"] = base64_encode($this->initial_data["tls"]); + } + + # Disable TLS if fasle + if ($this->initial_data["tls"] == "false") { + unset($this->validated_data["tls"]); + } + } + } + + private function __validate_tls_type() { + # Local variables + $key_types = ["auth", "crypt"]; + + # Validate TLS type if TLS set + if (isset($this->validated_data["tls"])) { + if (isset($this->initial_data["tls_type"])) { + if (in_array($this->initial_data["tls_type"], $key_types)) { + $this->validated_data["tls_type"] = $this->initial_data["tls_type"]; + } else { + $this->errors[] = APIResponse\get(2109); + } + } + } else { + unset($this->validated_data["tls_type"]); + } + } + + private function __validate_tls_keydir() { + # Local variables + $key_types = ["default", "direction-0", "direction-1", "both-directions"]; + + # Validate keydir direction if TLS set + if (isset($this->validated_data["tls"])) { + if (isset($this->initial_data["tlsauth_keydir"])) { + if (in_array($this->initial_data["tlsauth_keydir"], $key_types)) { + switch ($this->initial_data["tlsauth_keydir"]) { + case "default": + $this->validated_data["tlsauth_keydir"] = "default"; + break; + case "direction-0": + $this->validated_data["tlsauth_keydir"] = "0"; + break; + case "direction-1": + $this->validated_data["tlsauth_keydir"] = "1"; + break; + case "both-directions": + $this->validated_data["tlsauth_keydir"] = "2"; + break; + } + } else { + $this->errors[] = APIResponse\get(2110); + } + } + } else { + unset($this->validated_data["tlsauth_keydir"]); + } + } + + private function __validate_shared_key() { + # Check for our optional `Shared key` payload value + if (isset($this->initial_data["shared_key"])) { + # Auto generate Shared key if empty + if ($this->initial_data["shared_key"] === "") { + $this->validated_data["shared_key"] = base64_encode(openvpn_create_key()); + # Assume default if none was specified + } else { + $this->validated_data["shared_key"] = base64_encode($this->initial_data["shared_key"]); + } + } + } + + private function __validate_caref() { + if (isset($this->initial_data["caref"])) { + foreach ($this->config["ca"] as $ca) { + if ($this->initial_data["caref"] === $ca["refid"]) { + $this->validated_data["caref"] = $ca["refid"]; + break; + } + } + + if ($this->validated_data["caref"] !== $this->initial_data["caref"]) { + $this->errors[] = APIResponse\get(2142); + } + } + } + + private function __validate_crlref() { + if (isset($this->initial_data["crlref"])) { + if ($this->initial_data["crlref"] === "none") { + $this->validated_data["crlref"] = ""; + } else { + foreach ($this->config["crl"] as $crl) { + if (($this->validated_data["caref"] === $crl["caref"]) and ($this->initial_data["crlref"] === $crl["refid"])) { + $this->validated_data["crlref"] = $this->initial_data["refid"]; + break; + } + + if ((empty($this->validated_data["crlref"])) or ($this->validated_data["crlref"] !== $this->initial_data["crlref"])) { + $this->errors[] = APIResponse\get(2137); + } + } + } + } + } + + private function __validate_ocspcheck() { + # Check for our optional `OCSP` payload value + if (($this->initial_data["ocspcheck"] === true) and (empty($this->validated_data["ocspcheck"]))) { + $this->validated_data["ocspcheck"] = "yes"; + } elseif (($this->initial_data["ocspcheck"] === false) and (!empty($this->validated_data["ocspcheck"]))) { + unset($this->validated_data["ocspcheck"]); + } + } + + private function __validate_ocspurl() { + # Check for our optional `OCSP URL` payload value + if ($this->validated_data["ocspcheck"] === "yes") { + if (isset($this->initial_data["ocspurl"])) { + $this->validated_data["ocspurl"] = $this->initial_data["ocspurl"]; + } + } elseif (empty($this->validated_data["ocspcheck"])) { + unset($this->validated_data["ocspurl"]); + } + } + + private function __validate_certref() { + if (isset($this->initial_data["certref"])) { + foreach ($this->config["cert"] as $cert) { + if ($this->initial_data["certref"] === $cert["refid"]) { + $this->validated_data["certref"] = $cert["refid"]; + break; + } + } + + if ($this->validated_data["certref"] !== $this->initial_data["certref"]) { + $this->errors[] = APIResponse\get(2143); + } + } + } + + private function __validate_dh_length() { + # Local variables + $key_types = ["1024", "2048", "3072", "4096", "6144", "7680", "8192", "15360", "16384", "ECDH Only"]; + + # Validate DH Parameter Length + if (isset($this->initial_data["dh_length"])) { + if (in_array($this->initial_data["dh_length"], $key_types)) { + if ($this->initial_data["dh_length"] === "ECDH Only") { + $this->validated_data["dh_length"] = "none"; + } else { + $this->validated_data["dh_length"] = $this->initial_data["dh_length"]; + } + } else { + $this->errors[] = APIResponse\get(2111); + } + } + } + + private function __validate_ecdh_curve() { + # Local variables + $key_types = ["none", "prime256v1", "secp384r1", "secp521r1"]; + + # Validate ECDH Curve + if (isset($this->initial_data["ecdh_curve"])) { + if (in_array($this->initial_data["ecdh_curve"], $key_types)) { + $this->validated_data["ecdh_curve"] = $this->initial_data["ecdh_curve"]; + } else { + $this->errors[] = APIResponse\get(2112); + } + } + } + + private function __validate_cert_depth() { + # Local variables + $key_types = ["none", "one", "two", "three", "four", "five"]; + + # Validate Certificate Depth + if (isset($this->initial_data["cert_depth"])) { + if (in_array($this->initial_data["cert_depth"], $key_types)) { + switch ($this->initial_data["cert_depth"]) { + case "none": + $this->validated_data["cert_depth"] = ""; + break; + case "one": + $this->validated_data["cert_depth"] = "1"; + break; + case "two": + $this->validated_data["cert_depth"] = "2"; + break; + case "three": + $this->validated_data["cert_depth"] = "3"; + break; + case "four": + $this->validated_data["cert_depth"] = "4"; + break; + case "five": + $this->validated_data["cert_depth"] = "5"; + break; + } + } else { + $this->errors[] = APIResponse\get(2113); + } + } + } + + private function __validate_strick_user_cn() { + if (($this->initial_data['strictusercn'] === true) and ($this->validated_data["strictusercn"] === "")) { + $this->validated_data["strictusercn"] = "yes"; + } elseif (($this->initial_data['strictusercn'] === false) and ($this->validated_data["strictusercn"] === "yes")) { + $this->validated_data["strictusercn"] = ""; + } + } + + private function __validate_ncp_enable() { + if (($this->initial_data["data_encryption_negotiation"] === false) and ($this->validated_data["ncp_enable"] === "enabled")) { + $this->validated_data["ncp_enable"] = "disabled"; + } elseif (($this->initial_data["data_encryption_negotiation"] === true) and ($this->validated_data["ncp_enable"] === "disabled")) { + $this->validated_data["ncp_enable"] = "enabled"; + } + } + + private function __validate_data_ciphers() { + if (isset($this->initial_data["data_ciphers"])) { + $data_ciphers = explode(',', $this->initial_data['data_ciphers']); + $counter = 1; + foreach ($data_ciphers as $cipher) { + if (!array_key_exists($cipher, openvpn_get_cipherlist())) { + $this->errors[] = APIResponse\get(2138); + } elseif ($counter > 19) { + break; + } else { + $counter += 1; + } + } + $this->validated_data["data_ciphers"] = $this->initial_data['data_ciphers']; + } + } + + private function __validate_data_ciphers_fallback() { + # Validate Fallback Data Encryption Algorithm + if (isset($this->initial_data["data_ciphers_fallback"])) { + if (array_key_exists($this->initial_data["data_ciphers_fallback"], openvpn_get_cipherlist())) { + $this->validated_data["data_ciphers_fallback"] = $this->initial_data["data_ciphers_fallback"]; + } else { + $this->errors[] = APIResponse\get(2114); + } + } + } + + private function __validate_digest() { + # Local variables + $key_types = ["SHA256", "BLAKE2b512", "BLAKE2b256", "MD4", "MD5", "MD5-SHA1", "MDC2", "RIPEMD160", "SHA1", "SHA224", "SHA256", "SHA3-224", "SHA3-256", "SHA3-384", "SHA3-512", "SHA384", "SHA512", "SHA512-224", "SHA512-256", "SHAKE128", "SHAKE256", "SM3", "whirlpool", "none"]; + + # Validate Auth digest algorithm + if (isset($this->initial_data["digest"])) { + if (in_array($this->initial_data["digest"], $key_types)) { + $this->validated_data["digest"] = $this->initial_data["digest"]; + } else { + $this->errors[] = APIResponse\get(2115); + } + } + } + + private function __validate_engine() { + # Validate Hardware Crypto + if (isset($this->initial_data["engine"])) { + if (array_key_exists($this->initial_data["engine"], openvpn_get_engines())) { + $this->validated_data["engine"] = $this->initial_data["engine"]; + } else { + $this->errors[] = APIResponse\get(2116); + } + } + } + + private function __validate_tunnel_network() { + # Validate Tunnel Network + if (isset($this->initial_data['tunnel_network'])) { + if (!openvpn_validate_cidr($this->initial_data['tunnel_network'], "", false, "ipv4")) { + if (!openvpn_is_tunnel_network_in_use($this->initial_data['tunnel_network'])) { + $this->validated_data["tunnel_network"] = $this->initial_data['tunnel_network']; + } else { + $this->errors[] = APIResponse\get(2117); + } + } else { + $this->errors[] = APIResponse\get(2117); + } + } + } + + private function __validate_tunnel_networkv6() { + # Validate Tunnel Network v6 + if (isset($this->initial_data['tunnel_networkv6'])) { + if (!openvpn_validate_cidr($this->initial_data['tunnel_networkv6'], "", false, "ipv6")) { + if (!openvpn_is_tunnel_network_in_use($this->initial_data['tunnel_networkv6'])) { + $this->validated_data["tunnel_networkv6"] = $this->initial_data['tunnel_networkv6']; + } else { + $this->errors[] = APIResponse\get(2117); + } + } else { + $this->errors[] = APIResponse\get(2117); + } + } + } + + private function __validate_serverbridge_dhcp() { + if (($this->validated_data['dev_mode'] === 'tap') and (isset($this->initial_data["serverbridge_dhcp"]))) { + if ($this->initial_data['serverbridge_dhcp'] === true) { + $this->validated_data['serverbridge_dhcp'] = 'yes'; + } else { + $this->validated_data['serverbridge_dhcp'] = ""; + } + } + } + + private function __validate_serverbridge_interface() { + $key_types = ["wan", "lan", "none"]; + $match = false; + + if (($this->validated_data['serverbridge_dhcp'] === 'yes') and (isset($this->initial_data['serverbridge_interface']))) { + // Validate Virtual Interface + foreach ($this->config["virtualip"]["vip"] as $vip) { + if (strtolower($this->initial_data["serverbridge_interface"]) === strtolower($vip["descr"])) { + $this->validated_data["interface"] = "_vip" . $vip["uniqid"] . "|" . $vip["subnet"]; + $match = true; + break; + } elseif ((filter_var($vip["subnet"], FILTER_VALIDATE_IP)) and ($this->initial_data["serverbridge_interface"] === $vip["subnet"])) { + $this->validated_data["interface"] = "_vip" . $vip["uniqid"] . "|" . $vip["subnet"]; + $match = true; + break; + } elseif (strtolower($this->initial_data["serverbridge_interface"]) === strtolower($vip["uniqid"])) { + $this->validated_data["interface"] = "_vip" . $vip["uniqid"] . "|" . $vip["subnet"]; + $match = true; + break; + } + } + + if (in_array(strtolower($this->initial_data['serverbridge_interface']), $key_types)) { + $this->validated_data["serverbridge_interface"] = strtolower($this->initial_data['serverbridge_interface']); + $match = true; + } + + if (!$match) { + $this->errors[] = APIResponse\get(2146); + } + } + } + + private function __validate_serverbridge_routegateway() { + if (($this->validated_data['serverbridge_dhcp'] === 'yes') and (isset($this->initial_data['serverbridge_routegateway']))) { + if ($this->initial_data['serverbridge_routegateway'] === true) { + if ($this->validated_data["serverbridge_interface"] !== "none") { + $this->validated_data["serverbridge_routegateway"] = "yes"; + } else { + $this->errors[] = APIResponse\get(2142); + } + } else { + $this->validated_data["serverbridge_routegateway"] = ""; + } + } + } + + private function __validate_serverbridge_dhcp_start() { + if (($this->validated_data['serverbridge_dhcp'] === 'yes') and (isset($this->initial_data['serverbridge_dhcp_start']))) { + if ((isset($this->initial_data['serverbridge_dhcp_start'])) and (is_ipaddrv4($this->initial_data['serverbridge_dhcp_start'])) and (isset($this->initial_data['serverbridge_dhcp_end']))) { + $this->validated_data["serverbridge_dhcp_start"] = $this->initial_data['serverbridge_dhcp_start']; + } else { + $this->errors[] = APIResponse\get(2148); + } + } + } + + private function __validate_serverbridge_dhcp_end() { + if (($this->validated_data['serverbridge_dhcp'] === 'yes') and (isset($this->initial_data['serverbridge_dhcp_end']))) { + if ((isset($this->initial_data['serverbridge_dhcp_end'])) and (is_ipaddrv4($this->initial_data['serverbridge_dhcp_end'])) and (isset($this->initial_data['serverbridge_dhcp_start']))) { + $this->validated_data["serverbridge_dhcp_end"] = $this->initial_data['serverbridge_dhcp_end']; + } else { + $this->errors[] = APIResponse\get(2148); + } + } + } + + private function __validate_remote_network() { + # Check for our optional `Remote Network` payload value + if (isset($this->initial_data['remote_network'])) { + if (!openvpn_validate_cidr($this->initial_data['remote_network'], "", true, "ipv4")) { + $this->validated_data["remote_network"] = $this->initial_data['remote_network']; + } else { + $this->errors[] = APIResponse\get(2118); + } + } + } + + private function __validate_remote_networkv6() { + # Check for our optional `Remote Network v6` payload value + if (isset($this->initial_data['remote_networkv6'])) { + if (!openvpn_validate_cidr($this->initial_data['remote_networkv6'], "", true, "ipv6") === false) { + $this->validated_data["remote_networkv6"] = $this->initial_data['remote_networkv6']; + } else { + $this->errors[] = APIResponse\get(2118); + } + } + } + + private function __validate_redirect_gateway() { + if (isset($this->initial_data['redirect_gateway'])) { + if (($this->initial_data['redirect_gateway'] === true) and ($this->validated_data["gwredir"] === "")) { + $this->validated_data["gwredir"] = "yes"; + } elseif (($this->initial_data['redirect_gateway'] === false) and ($this->validated_data["gwredir"] === "yes")) { + $this->validated_data["gwredir"] = ""; + } + } + } + + private function __validate_redirect_gateway6() { + if (isset($this->initial_data['redirect_gateway6'])) { + if (($this->initial_data['redirect_gateway6'] === true) and ($this->validated_data["gwredir6"] === "")) { + $this->validated_data["gwredir6"] = "yes"; + } elseif (($this->initial_data['redirect_gateway6'] === false) and ($this->validated_data["gwredir6"] === "yes")) { + $this->validated_data["gwredir6"] = ""; + } + } + } + + private function __validate_local_network() { + if ($this->validated_data['gwredir'] === "") { + if (isset($this->initial_data['local_network'])) { + if (!openvpn_validate_cidr($this->initial_data['local_network'], "", true, "ipv4")) { + $this->validated_data["local_network"] = $this->initial_data['local_network']; + } else { + $this->errors[] = APIResponse\get(2119); + } + } + } else { + $this->validated_data["local_network"] = ""; + } + } + + private function __validate_local_networkv6() { + if ($this->validated_data['gwredir6'] === "") { + if (isset($this->initial_data['local_networkv6'])) { + if (!openvpn_validate_cidr($this->initial_data['local_networkv6'], "", true, "ipv6")) { + $this->validated_data["local_networkv6"] = $this->initial_data['local_networkv6']; + } else { + $this->errors[] = APIResponse\get(2119); + } + } + } else { + $this->validated_data["local_networkv6"] = ""; + } + } + + private function __validate_concurrent_connections() { + if (isset($this->initial_data['concurrent_connections'])) { + if (is_numericint($this->initial_data['concurrent_connections']) and (int) $this->initial_data['concurrent_connections'] > 0) { + $this->validated_data["maxclients"] = $this->initial_data['concurrent_connections']; + } else { + $this->errors[] = APIResponse\get(2133); + } + } + } + + private function __validate_allow_compression() { + # Local variables + $key_types = ["asym", "no", "yes"]; + + # Validate Allow Compression + if (isset($this->initial_data["allow_compression"])) { + if (in_array($this->initial_data["allow_compression"], $key_types)) { + $this->validated_data["allow_compression"] = $this->initial_data["allow_compression"]; + } else { + $this->errors[] = APIResponse\get(2120); + } + } + } + + private function __validate_compression() { + # Local variables + $key_types = ["none", "stub", "stub-v2", "lz4", "lz4-v2", "lzo", "noadapt", "adaptive", "yes", "no"]; + + if (($this->validated_data["allow_compression"] === "yes") or ($this->validated_data["allow_compression"] === "asym")) { + # Validate Compression + if (isset($this->initial_data["compression"])) { + if (in_array($this->initial_data["compression"], $key_types)) { + $this->validated_data["compression"] = $this->initial_data["compression"]; + } else { + $this->errors[] = APIResponse\get(2121); + } + } + # Assume default if none was specified + } else { + $this->validated_data["compression"] = ""; + } + } + + private function __validate_compression_push() { + if (($this->initial_data['compression_push'] === true) and ($this->validated_data["compression_push"] === "")) { + $this->validated_data["compression_push"] = "yes"; + # Assume default if none was specified + } elseif (($this->initial_data['compression_push'] === false) and ($this->validated_data["compression_push"] === "yes")) { + $this->validated_data["compression_push"] = ""; + } + } + + private function __validate_type_of_service() { + if (($this->initial_data['passtos'] === true) and ($this->validated_data["passtos"] === "")) { + $this->validated_data["passtos"] = "yes"; + } elseif (($this->initial_data['passtos'] === false) and ($this->validated_data["passtos"] === "yes")) { + $this->validated_data["passtos"] = ""; + } + } + + private function __validate_inter_client_communication() { + if (($this->initial_data['client2client'] === true) and ($this->validated_data["client2client"] === "")) { + $this->validated_data["client2client"] = "yes"; + # Assume default if none was specified + } elseif (($this->initial_data['client2client'] === false) and ($this->validated_data["client2client"] === "yes")) { + $this->validated_data["client2client"] = ""; + } + } + + private function __validate_duplicate_connection() { + if (($this->initial_data['duplicate_cn'] === true) and (empty($this->validated_data["duplicate_cn"]))) { + $this->validated_data["duplicate_cn"] = ""; + } elseif (($this->initial_data['duplicate_cn'] === false) and (!empty($this->validated_data["duplicate_cn"]))) { + unset($this->validated_data["duplicate_cn"]); + } + } + + private function __validate_dynamic_ip() { + if (($this->initial_data['dynamic_ip'] === true) and ($this->validated_data["dynamic_ip"] === "")) { + $this->validated_data["dynamic_ip"] = "yes"; + } elseif (($this->initial_data['dynamic_ip'] === false) and ($this->validated_data["dynamic_ip"] === "yes")) { + $this->validated_data["dynamic_ip"] = ""; + } + } + + // TODO: Add validation for "topology" + private function __validate_topology() { + # Local variables + $key_types = ["subnet", "net30"]; + + # Validate Topology + if ($this->validated_data["dev_mode"] === "tap") { + $this->validated_data["topology"] = "subnet"; + } elseif (isset($this->initial_data["topology"])) { + if (in_array($this->initial_data["topology"], $key_types)) { + $this->validated_data["topology"] = $this->initial_data["topology"]; + } else { + $this->errors[] = APIResponse\get(2122); + } + } + } + + private function __validate_dns_domain() { + if (isset($this->initial_data['dns_domain'])) { + $this->validated_data["dns_domain"] = $this->initial_data['dns_domain']; + } + } + + private function __validate_dns_servers() { + if (isset($this->initial_data['dns_servers'])) { + $dns_servers = explode(',', $this->initial_data['dns_servers']); + $counter = 1; + foreach ($dns_servers as $server) { + if ($counter > 4) { + break; + } elseif (is_ipaddrv4(str_replace(" ", "", $server))) { + $this->validated_data["dns_server" . $counter] = str_replace(" ", "", $server); + $counter += 1; + } + } + } + } + + private function __validate_push_blockout_side_dns() { + if (($this->initial_data['push_blockoutsidedns'] === true) and (empty($this->validated_data["push_blockoutsidedns"]))) { + $this->validated_data["push_blockoutsidedns"] = "yes"; + } elseif (($this->initial_data['push_blockoutsidedns'] === false) and (!empty($this->validated_data["push_blockoutsidedns"]))) { + unset($this->validated_data["push_blockoutsidedns"]); + } + } + + private function __validate_inactive_seconds() { + if (isset($this->initial_data['inactive_seconds'])) { + if (is_numericint($this->initial_data['inactive_seconds']) and ((int) $this->initial_data['inactive_seconds']) >= 0) { + $this->validated_data["inactive_seconds"] = $this->initial_data['inactive_seconds']; + # Assume default if none was specified + } else { + $this->errors[] = APIResponse\get(2134); + } + } + } + + private function __validate_ping_method() { + # Local variables + $key_types = ["keepalive", "ping"]; + + # Validate Ping method + if (isset($this->initial_data["ping_method"])) { + if (in_array($this->initial_data["ping_method"], $key_types)) { + $this->validated_data["ping_method"] = $this->initial_data["ping_method"]; + } else { + $this->errors[] = APIResponse\get(2123); + } + } + } + + private function __validate_keepalive_interval() { + if ($this->validated_data["ping_method"] === "keepalive") { + if (isset($this->initial_data['keepalive_interval'])) { + if (is_numericint($this->initial_data['keepalive_interval']) and ((int) $this->initial_data['keepalive_interval'] >= 0)) { + $this->validated_data["keepalive_interval"] = $this->initial_data['keepalive_interval']; + } else { + $this->errors[] = APIResponse\get(2124); + } + } + # Assume default if none was specified + } else { + $this->validated_data["keepalive_interval"] = "10"; + } + } + + private function __validate_keepalive_timeout() { + if ($this->validated_data["ping_method"] === "keepalive") { + if (isset($this->initial_data['keepalive_timeout'])) { + if (is_numericint($this->initial_data['keepalive_timeout']) and ((int) $this->initial_data['keepalive_timeout'] >= 0)) { + $this->validated_data["keepalive_timeout"] = $this->initial_data['keepalive_timeout']; + } else { + $this->errors[] = APIResponse\get(2125); + } + } + # Assume default if none was specified + } else { + $this->validated_data["keepalive_timeout"] = "60"; + } + } + + private function __validate_ping_seconds() { + if ($this->validated_data["ping_method"] === "ping") { + if (isset($this->initial_data['ping_seconds'])) { + if (is_numericint($this->initial_data['ping_seconds']) and ((int) $this->initial_data['ping_seconds'] >= 0)) { + $this->validated_data["ping_seconds"] = $this->initial_data['ping_seconds']; + } else { + $this->errors[] = APIResponse\get(2126); + } + } + # Assume default if none was specified + } else { + $this->validated_data["ping_seconds"] = "10"; + } + } + + private function __validate_ping_action_seconds() { + if ($this->validated_data["ping_method"] === "ping") { + if (isset($this->initial_data['ping_action_seconds'])) { + if (is_numericint($this->initial_data['ping_action_seconds']) and ((int) $this->initial_data['ping_action_seconds'] >= 0)) { + $this->validated_data["ping_action_seconds"] = $this->initial_data['ping_action_seconds']; + } else { + $this->errors[] = APIResponse\get(2127); + } + } + # Assume default if none was specified + } else { + $this->validated_data["ping_action_seconds"] = "60"; + } + } + + private function __validate_ping_push() { + if ($this->validated_data["ping_method"] === "ping") { + if (($this->initial_data['ping_push'] === true) and ($this->validated_data["ping_push"] === "")) { + $this->validated_data["ping_push"] = "yes"; + } elseif (($this->initial_data['ping_push'] === false) and ($this->validated_data["ping_push"] === "yes")) { + $this->validated_data["ping_push"] = ""; + } + # Assume default if none was specified + } else { + $this->validated_data["ping_push"] = ""; + } + } + + private function __validate_ping_action_push() { + if ($this->validated_data["ping_method"] === "ping") { + if (($this->initial_data['ping_action_push'] === true) and ($this->validated_data["ping_action_push"] === "")) { + $this->validated_data["ping_action_push"] = "yes"; + } elseif (($this->initial_data['ping_action_push'] === false) and ($this->validated_data["ping_action_push"] === "yes")) { + $this->validated_data["ping_action_push"] = ""; + } + # Assume default if none was specified + } else { + $this->validated_data["ping_action_push"] = ""; + } + } + + private function __validate_ping_action() { + # Local variables + $key_types = ["ping_exit", "ping_restart"]; + + # Validate Ping restart or exit + if ($this->validated_data["ping_method"] === "ping") { + if (isset($this->initial_data["ping_action"])) { + if (in_array($this->initial_data["ping_action"], $key_types)) { + $this->validated_data["ping_action"] = $this->initial_data["ping_action"]; + } + else { + $this->errors[] = APIResponse\get(2128); + } + } + # Assume default if none was specified + } else { + $this->validated_data["ping_action"] = "ping_restart"; + } + } + + private function __validate_username_as_cn() { + if (($this->initial_data['username_as_common_name'] === true) and ($this->validated_data["username_as_common_name"] === "disabled")) { + $this->validated_data["username_as_common_name"] = "enabled"; + } elseif (($this->initial_data['username_as_common_name'] === false) and ($this->validated_data["username_as_common_name"] === "enabled")) { + $this->validated_data["username_as_common_name"] = "disabled"; + } + } + + private function __validate_udp_fast_io() { + if (($this->initial_data['udp_fast_io'] === true) and (empty($this->validated_data["udp_fast_io"]))) { + $this->validated_data["udp_fast_io"] = "yes"; + } elseif (($this->initial_data['udp_fast_io'] === false) and (!empty($this->validated_data["udp_fast_io"]))) { + unset($this->validated_data["udp_fast_io"]); + } + } + + private function __validate_exit_notify() { + # Local variables + $key_types = ["once", "twice", "none", ""]; + + # Validate Exit Notify + if (isset($this->initial_data["exit_notify"])) { + if (in_array($this->initial_data["exit_notify"], $key_types)) { + switch ($this->initial_data["exit_notify"]) { + case "none": + $this->validated_data["exit_notify"] = "none"; + break; + case "": + $this->validated_data["exit_notify"] = "none"; + break; + case "once": + $this->validated_data["exit_notify"] = "1"; + break; + case "twice": + $this->validated_data["exit_notify"] = "2"; + break; + } + } else { + $this->errors[] = APIResponse\get(2129); + } + } + } + + private function __validate_snd_rcv_buf() { + # Local variables + $key_types = ["64KiB", "128KiB", "256KiB", "512KiB", "1MiB", "2MiB", "default"]; + + # Validate Send/Receive Buffer + if (isset($this->initial_data["sndrcvbuf"])) { + if (in_array($this->initial_data["sndrcvbuf"], $key_types)) { + switch ($this->initial_data["sndrcvbuf"]) { + case "64KiB": + $this->validated_data["sndrcvbuf"] = "65536"; + break; + case "128KiB": + $this->validated_data["sndrcvbuf"] = "131072"; + break; + case "256KiB": + $this->validated_data["sndrcvbuf"] = "262144"; + break; + case "512KiB": + $this->validated_data["sndrcvbuf"] = "524288"; + break; + case "1MiB": + $this->validated_data["sndrcvbuf"] = "1048576"; + break; + case "2MiB": + $this->validated_data["sndrcvbuf"] = "2097152"; + break; + case "default": + $this->validated_data["sndrcvbuf"] = ""; + break; + } + } else { + $this->errors[] = APIResponse\get(2130); + } + } + } + + private function __validate_push_register_dns() { + if (($this->initial_data['push_register_dns'] === true) and (empty($this->validated_data["push_register_dns"]))) { + $this->validated_data["push_register_dns"] = "yes"; + } elseif (($this->initial_data['push_register_dns'] === false) and (!empty($this->validated_data["push_register_dns"]))) { + unset($this->validated_data["push_register_dns"]); + } + } + + private function __validate_ntp_servers() { + if (isset($this->initial_data['ntp_servers'])) { + $ntp_servers = explode(',', $this->initial_data['ntp_servers']); + $counter = 1; + foreach ($ntp_servers as $server) { + if ($counter > 2) { + break; + } elseif (is_ipaddrv4(str_replace(" ", "", $server))) { + $this->validated_data["ntp_server" . $counter] = str_replace(" ", "", $server); + $counter += 1; + } else { + $this->errors[] = APIResponse\get(2149); + } + } + } + } + + private function __validate_netbios_enable() { + if (($this->initial_data['netbios_enable'] === true) and ($this->validated_data["netbios_enable"] === "")) { + $this->validated_data["netbios_enable"] = "yes"; + } elseif (($this->initial_data['netbios_enable'] === false) and ($this->validated_data["netbios_enable"] === "yes")) { + $this->validated_data["netbios_enable"] = ""; + } + } + + private function __validate_netbios_ntype() { + if (isset($this->initial_data['netbios_node_type'])) { + if ($this->initial_data['netbios_node_type'] == 'b') { + $this->validated_data["netbios_ntype"] = "1"; + } else if ($this->initial_data['netbios_node_type'] == 'p') { + $this->validated_data["netbios_ntype"] = "2"; + } else if ($this->initial_data['netbios_node_type'] == 'm') { + $this->validated_data["netbios_ntype"] = "3"; + } else if ($this->initial_data['netbios_node_type'] == 'h') { + $this->validated_data["netbios_ntype"] = "4"; + } else { + $this->errors[] = APIResponse\get(2076); + } + } + } + + private function __validate_netbios_scope() { + if (isset($this->initial_data['netbios_scope'])) { + $this->validated_data["netbios_scope"] = $this->initial_data['netbios_scope']; + } + } + + private function __validate_create_gw() { + # Local variables + $key_types = ["both", "v4only", "v6only"]; + + # Validate Gateway creation + if (isset($this->initial_data["create_gw"])) { + if (in_array(strtolower($this->initial_data["create_gw"]), $key_types)) { + switch ($this->initial_data["create_gw"]) { + case "both": + $this->validated_data["create_gw"] = strtolower($this->initial_data["create_gw"]); + break; + case "v4only": + $this->validated_data["create_gw"] = strtolower($this->initial_data["create_gw"]); + break; + case "v6only": + $this->validated_data["create_gw"] = strtolower($this->initial_data["create_gw"]); + break; + } + } else { + $this->errors[] = APIResponse\get(2131); + } + } + } + + private function __validate_verbosity_level() { + if (isset($this->initial_data['verbosity_level'])) { + if (is_numericint($this->initial_data['verbosity_level']) and ((int) $this->initial_data['verbosity_level'] >= 2) and ((int) $this->initial_data['verbosity_level'] < 13)) { + $this->validated_data["verbosity_level"] = $this->initial_data["verbosity_level"]; + } elseif ($this->initial_data['verbosity_level'] == "none") { + $this->validated_data["verbosity_level"] = "0"; + } elseif ($this->initial_data['verbosity_level'] == "default") { + $this->validated_data["verbosity_level"] = "1"; + } else { + $this->errors[] = APIResponse\get(2132); + } + } + } + + private function __validate_wins_servers() { + if (isset($this->initial_data['wins_servers'])) { + $win_servers = explode(',', $this->initial_data['wins_servers']); + $counter = 1; + foreach ($win_servers as $server) { + if ($counter > 2) { + break; + } elseif (is_ipaddrv4(str_replace(" ", "", $server))) { + $this->validated_data["wins_server" . $counter] = str_replace(" ", "", $server); + $counter += 1; + } else { + $this->errors[] = APIResponse\get(2151); + } + } + } + } + + // TODO: Validate nbdd servers is not yet available + private function __validate_nbdd_server() { + if (isset($this->validated_data['wins_servers' . '1']) and ($this->validated_data["netbios_enable"] = "yes")) { + $this->validated_data["nbdd_server1"] = ""; + } else { + unset($this->validated_data["nbdd_server1"]); + } + } + + private function __validate_disabled() { + # Check for our optional `disabled` payload value + if (($this->initial_data["disable"] === true) and (empty($this->validated_data["disable"]))) { + $this->validated_data["disable"] = ""; + } elseif (($this->initial_data["disable"] === false) and (!empty($this->validated_data["disable"]))) { + unset($this->validated_data["disable"]); + } + } + + public function validate_payload() { + # Run each validation method + $this->__validate_description(); + $this->__validate_disabled(); + $this->__validate_vpnid(); + $this->__validate_mode(); + $this->__validate_dev_mode(); + $this->__validate_protocol(); + $this->__validate_interface(); + $this->__validate_local_port(); + $this->__validate_ncp_enable(); + $this->__validate_data_ciphers(); + $this->__validate_data_ciphers_fallback(); + $this->__validate_engine(); + $this->__validate_digest(); + $this->__validate_tunnel_network(); + $this->__validate_tunnel_networkv6(); + $this->__validate_concurrent_connections(); + $this->__validate_allow_compression(); + $this->__validate_compression(); + $this->__validate_type_of_service(); + $this->__validate_ping_method(); + $this->__validate_keepalive_interval(); + $this->__validate_keepalive_timeout(); + $this->__validate_ping_seconds(); + $this->__validate_ping_action_seconds(); + $this->__validate_ping_push(); + $this->__validate_ping_action_push(); + $this->__validate_ping_action(); + $this->__validate_custom_options(); + $this->__validate_udp_fast_io(); + $this->__validate_snd_rcv_buf(); + $this->__validate_create_gw(); + $this->__validate_verbosity_level(); + + # Run each validation method for ["p2p_tls", "server_tls", "server_user", "server_tls_user"] + if ($this->validated_data["mode"] !== "p2p_shared_key") { + $this->__validate_tls(); + $this->__validate_tls_type(); + $this->__validate_tls_keydir(); + $this->__validate_caref(); + $this->__validate_crlref(); + $this->__validate_ocspcheck(); + $this->__validate_ocspurl(); + $this->__validate_certref(); + $this->__validate_dh_length(); + $this->__validate_ecdh_curve(); + $this->__validate_cert_depth(); + $this->__validate_redirect_gateway(); + $this->__validate_redirect_gateway6(); + $this->__validate_local_network(); + $this->__validate_local_networkv6(); + $this->__validate_compression_push(); + $this->__validate_inter_client_communication(); + $this->__validate_duplicate_connection(); + $this->__validate_dynamic_ip(); + $this->__validate_topology(); + $this->__validate_inactive_seconds(); + $this->__validate_exit_notify(); + + if (isset($this->validated_data["shared_key"])) { + unset($this->validated_data["shared_key"]); + } + } + + # Run each validation method for ["p2p_tls", "p2p_shared_key"] + if (($this->validated_data["mode"] === "p2p_tls") or ($this->validated_data["mode"] === "p2p_shared_key")) { + $this->__validate_remote_network(); + $this->__validate_remote_networkv6(); + + $this->validated_data["serverbridge_dhcp"] = ""; + $this->validated_data["serverbridge_interface"] = "none"; + $this->validated_data["serverbridge_routegateway"] = ""; + $this->validated_data["serverbridge_dhcp_start"] = ""; + $this->validated_data["serverbridge_dhcp_end"] = ""; + $this->validated_data["netbios_enable"] = ""; + $this->validated_data["netbios_ntype"] = "0"; + $this->validated_data["netbios_scope"] = ""; + + if (isset($this->validated_data["dns_domain"])) { + unset($this->validated_data["dns_domain"]); + } + + for ($i=1; $i < 5; $i++) { + if (isset($this->validated_data["dns_server" . $i])) { + unset($this->validated_data["dns_server" . $i]); + } + } + + for ($i=1; $i < 3; $i++) { + if (isset($this->validated_data["ntp_server" . $i])) { + unset($this->validated_data["ntp_server" . $i]); + } + } + + for ($i=1; $i < 3; $i++) { + if (isset($this->validated_data["wins_server" . $i])) { + unset($this->validated_data["wins_server" . $i]); + } + } + + if (isset($this->validated_data["push_blockoutsidedns"])) { + unset($this->validated_data["push_blockoutsidedns"]); + } + + if (isset($this->validated_data["push_register_dns"])) { + unset($this->validated_data["push_register_dns"]); + } + } + + # Run each validation method for ["server_tls", "server_user", "server_tls_user"] + if (($this->validated_data["mode"] !== "p2p_tls") and ($this->validated_data["mode"] !== "p2p_shared_key")) { + $this->__validate_dns_domain(); + $this->__validate_dns_servers(); + $this->__validate_push_blockout_side_dns(); + $this->__validate_push_register_dns(); + $this->__validate_ntp_servers(); + $this->__validate_netbios_enable(); + $this->__validate_netbios_ntype(); + $this->__validate_netbios_scope(); + $this->__validate_wins_servers(); + $this->__validate_nbdd_server(); + $this->__validate_serverbridge_dhcp(); + $this->__validate_serverbridge_interface(); + $this->__validate_serverbridge_routegateway(); + $this->__validate_serverbridge_dhcp_start(); + $this->__validate_serverbridge_dhcp_end(); + + $this->validated_data["remote_network"] = ""; + $this->validated_data["remote_networkv6"] = ""; + } + + # Run each validation method for ["p2p_tls", "p2p_shared_key", "server_tls"] + if (($this->validate_data["mode"] !== "server_user") and ($this->validated_data["mode"] !== "server_tls_user")) { + $this->validated_data["username_as_common_name"] = "disabled"; + + if (isset($this->validated_data["authmode"])) { + unset($this->validated_data["authmode"]); + } + } + + # Run each validation method for ["server_user", "server_tls_user"] + if (($this->validate_data["mode"] === "server_user") or ($this->validated_data["mode"] === "server_tls_user")) { + $this->__validate_authmode(); + $this->__validate_username_as_cn(); + } + + if ($this->validated_data['mode'] !== 'server_tls_user') { + unset($this->validated_data["strictusercn"]); + } + + # Run each validation method for ["p2p_tls"] + if ($this->validated_data["mode"] === "p2p_tls") { + } + + # Run each validation method for ["p2p_shared_key"] + if ($this->validated_data["mode"] === "p2p_shared_key") { + $this->__validate_shared_key(); + + $this->validated_data["gwredir"] = ""; + $this->validated_data["gwredir6"] = ""; + $this->validated_data["local_network"] = ""; + $this->validated_data["local_networkv6"] = ""; + $this->validated_data["compression_push"] = ""; + $this->validated_data["client2client"] = ""; + $this->validated_data["topology"] = "subnet"; + $this->validated_data["dynamic_ip"] = ""; + $this->validated_data["exit_notify"] = "1"; + $this->validated_data["inactive_seconds"] = "300"; + + if (isset($this->validated_data["tls"])) { + unset($this->validated_data["tls"]); + } + + if (isset($this->validated_data["tls_type"])) { + unset($this->validated_data["tls_type"]); + } + + if (isset($this->validated_data["tlsauth_keydir"])) { + unset($this->validated_data["tlsauth_keydir"]); + } + + if (isset($this->validated_data["caref"])) { + unset($this->validated_data["caref"]); + } + + if (isset($this->validated_data["certref"])) { + unset($this->validated_data["certref"]); + } + + if (isset($this->validated_data["crlref"])) { + unset($this->validated_data["crlref"]); + } + + if (isset($this->validated_data["cert_depth"])) { + unset($this->validated_data["cert_depth"]); + } + + if (isset($this->validated_data["ocspcheck"])) { + unset($this->validated_data["ocspcheck"]); + } + + if (isset($this->validated_data["ocspurl"])) { + unset($this->validated_data["ocspurl"]); + } + + if (isset($this->validated_data["dh_length"])) { + unset($this->validated_data["dh_length"]); + } + + if (isset($this->validated_data["ecdh_curve"])) { + unset($this->validated_data["ecdh_curve"]); + } + + if (isset($this->validated_data["duplicate_cn"])) { + unset($this->validated_data["duplicate_cn"]); + } + } + + # Run each validation method for ["server_tls"] + if ($this->validated_data["mode"] === "server_tls") { + } + + # Run each validation method for ["server_user"] + if ($this->validate_data["mode"] === "server_user") { + } + + # Run each validation method for ["server_tls_user"] + if ($this->validated_data["mode"] === "server_tls_user") { + $this->__validate_strick_user_cn(); + } + } + + private function __init_config() { + # Ensure the OpenVPN configuration array exists + if (empty($this->config["openvpn"])) { + $this->config["openvpn"] = []; + } + + # Ensure the OpenVPN server configuration array exists + if (empty($this->config['openvpn']['openvpn-server'])) { + $this->config['openvpn']['openvpn-server'] = []; + } + } +} diff --git a/pfSense-pkg-API/files/usr/local/www/api/documentation/openapi.yml b/pfSense-pkg-API/files/usr/local/www/api/documentation/openapi.yml index 9ca90087b..cf0785a09 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/documentation/openapi.yml +++ b/pfSense-pkg-API/files/usr/local/www/api/documentation/openapi.yml @@ -6535,6 +6535,1515 @@ paths: summary: Add NTP (ntpd) time server tags: - Services > NTPD > Time Server + /api/v1/services/openvpn/server: + delete: + description: 'Delete OpenVPN Server instance.

+ + _Requires at least one of the following privileges:_ [`page-all`, `page-openvpn-server`]' + parameters: + - description: This id indicates the id of the server you want to delete. + This id is static and not an index of an entry in the config array. + Best to first get all servers and get the id of the one you want to + delete before calling this endpoint. + in: query + name: vpnid + required: true + schema: + oneOf: + - type: integer + - type: string + requestBody: + content: + application/json: + schema: + properties: + vpnid: + description: This id indicates the id of the server you want to delete. + This id is static and not an index of an entry in the config array. + Best to first get all servers and get the id of the one you want to + delete before calling this endpoint. + oneOf: + - type: integer + - type: string + example: "1" + required: + - vpnid + type: object + responses: + 200: + $ref: '#/components/responses/Success' + 401: + $ref: '#/components/responses/AuthenticationFailed' + summary: Delete OpenVPN Server + tags: + - Services > OpenVPN > Server + get: + description: 'Read All OpenVPN Server instances.

+ + _Requires at least one of the following privileges:_ [`page-all`, `page-openvpn-server`]' + responses: + 200: + $ref: '#/components/responses/Success' + 401: + $ref: '#/components/responses/AuthenticationFailed' + summary: Read OpenVPN Server + tags: + - Services > OpenVPN > Server + post: + description: 'Create OpenVPN Server instance.

+ + _Requires at least one of the following privileges:_ [`page-all`, `page-openvpn-server`]' + requestBody: + content: + application/json: + schema: + properties: + mode: + default: 'p2p_tls' + description: The role for the server, which specifies how peers connect to a + server instance. Changing this also affects which options the + GUI displays on the rest of the page. + enum: + - p2p_tls + - p2p_shared_key + - server_tls + - server_user + - server_tls_user + type: string + protocol: + default: 'udp4' + description: The IP protocol clients will use to connect to this VPN. + enum: + - udp4 + - udp6 + - tcp4 + - tcp6 + - udp + - tcp + type: string + authmode: + default: 'Local Database' + description: One or more authentication servers to use when checking user credentials. + This can be the Local Database (user manager) or it can be a RADIUS or LDAP server + configured in the user manager. + + Selecting multiple entries will use each one in turn. If authentication against an entry fails, + the VPN will try the next server. It will continue this process until it has tried + all selected entries. + + This field is only present when using a Server Mode (`server_user` or `server_tls_user`) + which requires user authentication. + type: string + dev_mode: + default: 'tun' + description: OpenVPN can run in one of two device modes. + enum: + - tun + - tap + type: string + interface: + default: 'wan' + description: 'Selects the interface, VIP, or failover group that the OpenVPN instance will use + when communicating with peers. + This also controls which interface the traffic from the instance will exit. + Interface can be provided as Interface ID (e.g. `wan`, `lan`, `opt1`, etc.), + Interface physical name (e.g. `igb0`, `vtnet`, etc.) or Interface descriptive + name (e.g. `MY_LAN`, `MY_WAN`, etc.). + VIP can be provided as description, subnet or uniqid. + Failover group can be provided as group name. + + _Note: If you are using a failover group, the failover group must be configured to use the same_.' + type: string + local_port: + description: The port number upon which OpenVPN will listen for incoming connections from peers. + Firewall rules must allow traffic to this port and this port must be specified in the client configuration. + + The port for each server must be unique for each interface when using a standard + UDP or TCP Protocol choice and must be globally unique if using a multihome Protocol. + + Omitting this will default to next available port. + type: integer + example: 1194 + description: + description: Enter a description for this VPN instance, for reference. + type: string + example: 'My VPN' + custom_options: + description: Custom options may be added in using the Custom option box separated by a semicolon (;) + and the firewall will pass the custom directives to OpenVPN. + type: string + example: 'push "route 10.0.0.0 255.255.255.0";' + tls: + default: '' + description: TLS, or Transport Layer Security, can provide session + authentication and encryption to ensure the validity of peers and to protect the control channel. + Paste the TLS key. This is not necessary for mode `p2p_shared_key`. + Options are `""` or `true` for automatic TLS key generation, `false` to disable TLS + or a raw TLS key as a string. + Omitting disable TLS configuration. + type: string + example: '-----BEGIN OpenVPN Static key V1----- + db8701afd882d746be67f084bae68470 + 54a99ef3b61864cfe1864c6c02584335 + fe706df150250bf7e294b8c35911817c + 133c8d9b505573ebeb65259bc54c70ae + 88cc3163fd11a20f73d2c6fb3eea7cc2 + fdaefcde510486adc0acbb9481b3aef4 + 930db91806469218b2a4f92e71787cf4 + 635f5b3f773fefc28d492738d2648673 + 8fa9b23d41e5999f58c3f0004a59ecda + 119162e598764d9c8f6b99e7054f3ea4 + fdb4461913d2e3273a8a0db29332406a + 237bcfccca2315445ef809eaa933fa30 + a9a7510d2d167033edfd3580a824f3e1 + 1af57da6eee89e6318ec29c67da8a19d + 7c9d74c7afac6ee0f813a0278a6261d7 + a28e7bdbf1743527346bda359bc92fc9 + -----END OpenVPN Static key V1-----' + tls_type: + default: 'auth' + description: Enter TLS Key Usage Mode. This is not necessary for mode `p2p_shared_key`. + enum: + - auth + - crypt + type: string + tlsauth_keydir: + default: 'default' + description: The direction in which this VPN endpoint uses the TLS key. + The TLS Key Direction must be set to complementary values on the server and clients. + The default behavior uses 0 on server instances and 1 on client instances. + + Enter TLS keydir direction. This is not necessary for mode `p2p_shared_key`. + enum: + - default + - direction-0 + - direction-1 + - both-directions + type: string + shared_key: + default: '' + description: Enter Shared Key. This is only mandatory for mode `p2p_shared_key`. + Options are `""` for automatic shared key generation and a raw shared key. + type: string + example: '-----BEGIN OpenVPN Static key V1----- + db8701afd882d746be67f084bae68470 + 54a99ef3b61864cfe1864c6c02584335 + fe706df150250bf7e294b8c35911817c + 133c8d9b505573ebeb65259bc54c70ae + 88cc3163fd11a20f73d2c6fb3eea7cc2 + fdaefcde510486adc0acbb9481b3aef4 + 930db91806469218b2a4f92e71787cf4 + 635f5b3f773fefc28d492738d2648673 + 8fa9b23d41e5999f58c3f0004a59ecda + 119162e598764d9c8f6b99e7054f3ea4 + fdb4461913d2e3273a8a0db29332406a + 237bcfccca2315445ef809eaa933fa30 + a9a7510d2d167033edfd3580a824f3e1 + 1af57da6eee89e6318ec29c67da8a19d + 7c9d74c7afac6ee0f813a0278a6261d7 + a28e7bdbf1743527346bda359bc92fc9 + -----END OpenVPN Static key V1-----' + caref: + description: Specify the unique reference ID of the certificate signing authority. + type: string + example: 61c7fbce3a351 + crlref: + default: 'none' + description: This optional field is for the Certificate Revocation List (CRL) + this tunnel will use to check the validity of peer certificates. + + A CRL is a list of certificates signed by a CA which are no longer valid. + This could be due to a certificate being compromised or lost, such as from + a stolen laptop, spyware infection, etc. + + Specify the unique reference ID of the certificate. + type: string + example: 61c467cd2fd4f + certref: + description: The certificate used by the VPN instance to identify itself to peers. + This certificate must contain appropriate properties marking it as a server + certificate and not a user or client certificate. + + Specify the unique reference ID of the certificate revocation list. + type: string + example: 61c4121240420 + ocspcheck: + default: false + description: When set, OpenVPN will attempt to confirm client certificate validity + using Online Certificate Status Protocol (OCSP) against the site listed in the OCSP URL field. + type: boolean + ocspurl: + default: '' + description: Enter OCSP URL. This is only necessary for `ocspcheck`. + type: string + format: uri + dh_length: + default: '2048' + description: The Diffie-Hellman (DH) key exchange parameters are used for establishing a + secure communications channel. They may be regenerated at any time, and they are not + specific to an OpenVPN instance. When importing an existing OpenVPN configuration + these parameters do not need to be copied from the previous server nor does it require + generating new parameters. The length of the desired DH parameters may be chosen from + the drop-down box, either 1024, 2048, or 4096. There is an additional choice for + ECDH Only which disables traditional DH parameters and uses only ECDH. + enum: + - 1024 + - 2048 + - 3072 + - 4096 + - 6144 + - 7680 + - 8192 + - 15360 + - 16384 + - 'ECDH Only' + type: string + ecdh_curve: + default: 'none' + description: Configures a specific elliptic curve to use for Elliptic Curve Diffie-Hellman + key exchanges. This is only for use with ECDH TLS encryption. + enum: + - none + - prime256v1 + - secp384r1 + - secp521r1 + type: string + cert_depth: + default: 'one' + description: This option limits the valid length of a certificate chain. The default value + limits a chain to One (Client+Server). With that value, if an unauthorized intermediate + CA signs a certificate, certificates signed by the rogue intermediate would fail validation. + In cases when the certificate stricture requires chaining with intermediates, + raise this limit to accommodate the longest allowed chain. + enum: + - none + - one + - two + - three + - four + - five + type: string + strictusercn: + default: false + description: Controls whether the firewall will enforce a strict match between the username + supplied by the user and the Common Name of their user certificate when the firewall + authenticates a user. When enabled, authentication fails if the two values do not match. + + This prevents users from using their own credentials with the certificate from a different + user and vice versa. + + This is only necessary for mode `server_tls_user`. + Omitting will disable. + type: boolean + ncp_enable: + default: false + description: Enable Data Encryption Negotiation. + Disabling this feature is deprecated. + Omitting will enable. + type: boolean + data_ciphers: + default: 'AES-256-GCM,AES-128-GCM,CHACHA20-POLY1305' + description: These options define the cryptographic ciphers OpenVPN will use for this VPN. + Enter comma sepereted data encryption algorithms + (e.g. `AES-256-GCM,AES-128-GCM,CHACHA20-POLY1305`). + Data encryption algorithms will be truncated to 19. + type: string + data_ciphers_fallback: + default: 'AES-256-CBC' + description: Enter fallback data encryption algorithm. + type: string + digest: + default: 'SHA256' + description: Selects the message digest algorithm OpenVPN uses for HMAC authentication of + incoming packets. This is used for the data channel and if also for the control channel + when the tunnel uses a TLS key. The GUI default of SHA256 is a good balance of security and speed. + + When using AEAD ciphers such as AES-GCM, OpenVPN ignores this value for the data channel + since AEAD ciphers already perform authentication. Even with an AEAD cipher, OpenVPN still + uses this algorithm to authenticate the control channel when the tunnel uses a TLS key. + enum: + - BLAKE2b512 + - BLAKE2b256 + - MD4 + - MD5 + - MD5-SHA1 + - MDC2 + - RIPEMD160 + - SHA1 + - SHA224 + - SHA256 + - SHA3-224 + - SHA3-256 + - SHA3-384 + - SHA3-512 + - SHA384 + - SHA512 + - SHA512-224 + - SHA512-256 + - SHAKE128 + - SHAKE256 + - SM3 + - whirlpool + - none + type: string + engine: + default: 'none' + description: If available, this option controls which hardware cryptographic accelerator + will be used by OpenVPN. When left unspecified, OpenVPN will choose automatically based + on what is available in the operating system to accelerate ciphers OpenVPN wants to use. + + Some hardware acceleration, such as AES-NI, happens automatically in OpenVPN via OpenSSL + and cannot be enabled or disabled by this option. + + Enter Hardware crypto. Options are `none` and any enabled + hardware crypto. + type: string + tunnel_network: + default: '' + description: 'Virtual IPv4 network used for private communications + between this client and the server expressed using CIDR (e.g. + 10.0.8.5/24). + + With subnet topology, enter the client IP address and the subnet + mask must match the IPv4 Tunnel Network on the server. + + With net30 topology, the first network address of the /30 is assumed + to be the server address and the second network address will be + assigned to the client.' + type: string + tunnel_networkv6: + default: '' + description: 'Virtual IPv6 network used for private communications + between this client and the server expressed using prefix (e.g. + 2001:db9:1:1::100/64). + + Enter the client IPv6 address and prefix. The prefix must match + the IPv6 Tunnel Network prefix on the server.' + type: string + local_network: + default: '' + description: 'IPv4 server-side networks that will be accessible + from this particular client. Expressed as a comma-separated list + of one or more CIDR networks. + + NOTE: Networks do not need to be specified here if they have already + been defined on the main server configuration.' + type: string + local_networkv6: + default: '' + description: 'IPv6 server-side networks that will be accessible + from this particular client. Expressed as a comma-separated list + of one or more IP/PREFIX networks. + + NOTE: Networks do not need to be specified here if they have already + been defined on the main server configuration.' + type: string + remote_network: + default: '' + description: 'IPv4 client-side networks that will be routed to this + client specifically using iroute, so that a site-to-site VPN can + be established. Expressed as a comma-separated list of one or + more CIDR ranges. May be left blank if there are no client-side + networks to be routed. + + NOTE: Remember to add these subnets to the IPv4 Remote Networks + list on the corresponding OpenVPN server settings.' + type: string + remote_networkv6: + default: '' + description: 'IPv6 client-side networks that will be routed to this + client specifically using iroute, so that a site-to-site VPN can + be established. Expressed as a comma-separated list of one or + more IP/PREFIX networks. May be left blank if there are no client-side + networks to be routed. + + NOTE: Remember to add these subnets to the IPv6 Remote Networks + list on the corresponding OpenVPN server settings.' + type: string + redirect_gateway: + default: false + description: When a Redirect IPv4 Gateway option is selected the server + pushes a message to clients instructing them to forward all traffic for + that address family, including Internet traffic, over the VPN tunnel. + type: boolean + redirect_gateway6: + default: false + description: When a Redirect IPv6 Gateway option is selected the server + pushes a message to clients instructing them to forward all traffic for + that address family, including Internet traffic, over the VPN tunnel. + type: boolean + serverbridge_dhcp: + default: "" + description: When using `tap` mode for a remote access SSL/TLS VPN, + (`server_tls`, `server_user`, `server_tls_user`), additional options + to control bridging behavior in OpenVPN and client address assignment. + + When selected, OpenVPN passes through DHCP to the bridged. + In the most common scenario, this is LAN. + + Using this method connecting clients receive IP addresses from the + same DHCP pool used by clients on the LAN. + type: string + format: ipv4 + serverbridge_interface: + default: "none" + description: This setting indicates to OpenVPN which interface will be used for the bridge. + In most cases this is LAN. + This option controls which existing IP address and subnet mask are used by OpenVPN for the bridge. + Setting this to none will cause the Server Bridge DHCP (`serverbridge_dhcp_start`, `serverbridge_dhcp_end`) settings below to be ignored. + enum: + - wan + - lan + - none + type: string + serverbridge_routegateway: + description: Makes OpenVPN push the Bridge Interface IPv4 address to connecting + clients as a route gateway. + + When the IPv4 Tunnel Network in OpenVPN is empty for a bridged VPN, + connecting clients cannot automatically determine a server-side gateway for + use with routes pushed to clients by the server. + type: boolean + example: true + serverbridge_dhcp_start: + default: "" + description: Defines a DHCP range from which OpenVPN can assign addresses to clients. + If these settings are left blank, OpenVPN passes DHCP through to the bridge interface + and it ignores the interface setting above. + + When set, this range should be within the same subnet as the Bridge Interface + (`serverbridge_interface`) but it should not overlap an existing in-use portion + of the subnet, such as the current DHCP pool. + + This allows a range of IP addresses to be set aside for use only by OpenVPN clients so they may be contained within a portion of the internal network rather than consuming IP addresses from the existing DHCP pool. + type: string + format: ipv4 + serverbridge_dhcp_end: + default: "" + description: Defines a DHCP range from which OpenVPN can assign addresses to clients. + If these settings are left blank, OpenVPN passes DHCP through to the bridge interface + and it ignores the interface setting above. + + When set, this range should be within the same subnet as the Bridge Interface + (`serverbridge_interface`) but it should not overlap an existing in-use portion + of the subnet, such as the current DHCP pool. + + This allows a range of IP addresses to be set aside for use only by OpenVPN clients so they may be contained within a portion of the internal network rather than consuming IP addresses from the existing DHCP pool. + type: string + format: ipv4 + concurrent_connections: + description: Specifies the number of clients that may be simultaneously + connected to this OpenVPN server instance at any given time. + type: integer + example: '10' + allow_compression: + default: 'no' + description: Controls whether or not compression is allowed on the VPN, and how it is handled. + enum: + - asym + - no + - yes + type: string + compression: + default: 'none' + description: Compression to be used with this VPN instance. This is only + necessary for `allow_compression` set to `yes` or `asym`. + Compression can potentially dangerous and insecure. Deprecated. + Compress tunnel packets using the LZO algorithm. + enum: + - none + - stub + - stub-v2 + - lz4 + - lz4-v2 + - lzo + - noadapt + - adaptive + - yes + - no + type: string + compression_push: + default: false + description: Push the selected Compression setting to connecting clients. + This is only necessary for `allow_compression` set to `yes` or `asym`. + type: boolean + passtos: + default: false + description: When this option is enabled OpenVPN sets the Type-of-Service (TOS) + IP header value of tunnel packets to match the encapsulated packet value. + This may cause important traffic to be handled faster over the tunnel by + intermediate hops at the cost of minor information disclosure. + type: boolean + client2client: + default: false + description: This option controls whether or not connected clients are able to + communicate with one another. To allow this behavior, check the option. + When unchecked, clients can only send traffic to the server or destinations + beyond the server such as routed networks or the Internet. + type: boolean + duplicate_cn: + default: false + description: Controls whether or not OpenVPN will allow multiple connections + from the same user to work simultaneously. + This is only necessary for mode `p2p_shared_key`. + type: boolean + dynamic_ip: + default: false + description: For clients on Internet connections where the IP address changes frequently, + or mobile users who commonly move between different Internet connections, + check this option for more stable connectivity. Where the client IP address + is static or rarely changes, not using this option offers a small security improvement. + type: boolean + topology: + default: 'subnet' + description: Sets the method OpenVPN uses to allocate addresses for clients in a + client/server setup on tun device mode VPNs. The Topology option is relevant + only when supplying a virtual adapter IP address to clients using tun mode on IPv4. + Some clients may require this even for IPv6, such as OpenVPN Connect, though in + reality IPv6 always runs with a subnet topology even when IPv4 uses net30. + OpenVPN instances using tap mode always use subnet topology as well. + enum: + - subnet + - net30 + type: string + dns_domain: + default: '' + description: Configures a default domain name which clients will append to DNS requests. + This can be helpful to ensure name resolution works properly for hosts on the local + network where DNS name resolution is used. + type: string + example: example.com + dns_servers: + default: '' + description: Comma-separated list of no more than 4 DNS server IP + addresses (i.e. 8.8.8.8). Any more than the first four are ignored. + This list is provided to the clent. + type: string + example: "8.8.8.8, 8.8.4.4, 8.8.3.3, 8.8.2.2" + push_blockoutsidedns: + default: false + description: Make Windows 10 Clients Block access to DNS servers except + across OpenVPN while connected, forcing clients to use only VPN DNS servers. + type: boolean + push_register_dns: + default: false + description: Run "net stop dnscache", "net start dnscache", + "ipconfig /flushdns" and "ipconfig /registerdns" on connection initiation. + type: boolean + inactive_seconds: + default: 300 + description: The amount of time, in seconds, which a client can be inactive before + OpenVPN disconnects it for inactivity. OpenVPN bases activity on the last incoming + or outgoing data channel packet, not control channel packets. + + `0` disables this feature. + type: integer + ping_method: + default: 'keepalive' + description: The Ping Method controls OpenVPN monitoring of peers through the + control channel and how it deals with unresponsive peers. + enum: + - keepalive + - ping + type: string + keepalive_interval: + default: 10 + description: Enter keepalive interval. + type: integer + keepalive_timeout: + default: 60 + description: Enter keepalive timeout. + type: integer + ping_seconds: + default: 10 + description: Enter ping seconds. This is only necessary for `ping` method. + type: integer + ping_action_seconds: + default: 60 + description: Enter ping action seconds. This is only necessary for `ping` method. + type: integer + ping_push: + default: false + description: Push ping to VPN client. This is only necessary for `ping` method. + type: boolean + ping_action_push: + default: false + description: Push ping-restart/ping-exit to VPN client. + This is only necessary for `ping` method. + type: boolean + ping_action: + default: 'ping_restart' + description: Exit or restart OpenVPN after timeout from remote. + This is only necessary for `ping` method. + enum: + - ping_restart + - ping_exit + type: string + username_as_common_name: + default: false + description: Controls whether or not OpenVPN will use the username given by the + client in place of the certificate common name (CN) for purposes such as determining + Client Specific Overrides. This is only relevant when user authentication is enabled. + This is typically the best practice, but not a requirement. + + This is only necessary for mode `server_user` or `server_tls_user`. + type: boolean + udp_fast_io: + default: false + description: Controls whether or not OpenVPN will use fast I/O operations with UDP writes + to its tun or tap device. This behavior optimizes the packet write event loop, + improving CPU efficiency by 5% to 10%. Experimental. + type: boolean + exit_notify: + default: 'none' + description: Controls whether or not OpenVPN will send an explicit exit notification to + connected UDP clients or peers when restarting or shutting down. This notification + allows peers to immediately disconnect rather than wait for a timeout. + This is only relevant to UDP modes as TCP natively supports closing connections. + enum: + - once + - twice + - none + - '' + type: string + sndrcvbuf: + default: 'default' + description: Configures a Send and Receive Buffer size for OpenVPN. + The default buffer size can be too small in many cases, depending on hardware + and network uplink speeds. Finding the best buffer size can take experimentation. + To test the best value for a site, start at 512KiB and test higher and lower + values until testing results in peak performance. + enum: + - 64KiB + - 128KiB + - 256KiB + - 512KiB + - 1MiB + - 2MiB + - default + - none + type: string + ntp_servers: + default: '' + description: Comma-separated list of no more than 2 NTP server IP + addresses. Any more than the first two are ignored. + type: string + example: "192.168.56.101, 192.168.56.102" + netbios_enable: + default: false + description: Enable NetBIOS over TCP/IP. If this option is not set, + all NetBIOS-over-TCP/IP options (including WINS) will be disabled. + type: boolean + netbios_node_type: + description: 'Possible options: b (broadcasts), p (point-to-point + name queries to a WINS server), m (broadcast then query name server), + and h (query name server, then broadcast). This parameter takes + a single letter either b, p, m, or h. Any other string will result + in this option being set to none.' + enum: + - b + - p + - m + - h + type: string + example: b + netbios_scope: + description: NetBIOS Scope ID provides an extended naming service + for NetBIOS over TCP/IP. The NetBIOS scope ID isolates NetBIOS + traffic on a single network to only those nodes with the same + NetBIOS scope ID. + type: string + example: '5' + create_gw: + default: 'both' + description: If you assign a virtual interface to this OpenVPN server, + this setting controls which gateway types will be created. + enum: + - v4only + - v6only + - both + type: string + verbosity_level: + default: '3' + description: Configures the amount of detail OpenVPN will log for this instance, + which is useful for troubleshooting problems. Higher numbers will result in + higher amounts of detail in the log. + During normal operation the default selection is ideal. + Level `3` is recommended for a good summary of what's happening without + being swamped by output. Maximum level `11`. + The `default` setting is equivalent to level `1`. + enum: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - 'default' + type: string + wins_servers: + default: '' + description: Comma-separated list of no more than 2 WINS server + IP addresses. Any more than the first two are ignored. + type: string + example: "192.168.56.103, 192.168.56.104" + disable: + default: false + description: Disable this override without removing it. + type: boolean + type: object + responses: + 200: + $ref: '#/components/responses/Success' + 401: + $ref: '#/components/responses/AuthenticationFailed' + summary: Create OpenVPN Server + tags: + - Services > OpenVPN > Server + put: + description: 'Update OpenVPN Server instance.

+ + _Requires at least one of the following privileges:_ [`page-all`, `page-openvpn-server`]' + parameters: + - description: This id indicates the id of the server you want to delete. + This id is static and not an index of an entry in the config array. + Best to first get all servers and get the id of the one you want to + delete before calling this endpoint. + in: query + name: vpnid + required: true + schema: + oneOf: + - type: integer + - type: string + requestBody: + content: + application/json: + schema: + properties: + vpnid: + description: This id indicates the id of the server you want to delete. + This id is static and not an index of an entry in the config array. + Best to first get all servers and get the id of the one you want to + delete before calling this endpoint. + type: string + example: '1' + mode: + default: 'p2p_tls' + description: The role for the server, which specifies how peers connect to a + server instance. Changing this also affects which options the + GUI displays on the rest of the page. + enum: + - p2p_tls + - p2p_shared_key + - server_tls + - server_user + - server_tls_user + type: string + protocol: + default: 'udp4' + description: The IP protocol clients will use to connect to this VPN. + enum: + - udp4 + - udp6 + - tcp4 + - tcp6 + - udp + - tcp + type: string + authmode: + default: 'Local Database' + description: One or more authentication servers to use when checking user credentials. + This can be the Local Database (user manager) or it can be a RADIUS or LDAP server + configured in the user manager. + + Selecting multiple entries will use each one in turn. If authentication against an entry fails, + the VPN will try the next server. It will continue this process until it has tried + all selected entries. + + This field is only present when using a Server Mode (`server_user` or `server_tls_user`) + which requires user authentication. + type: string + dev_mode: + default: 'tun' + description: OpenVPN can run in one of two device modes. + enum: + - tun + - tap + type: string + interface: + default: 'wan' + description: 'Selects the interface, VIP, or failover group that the OpenVPN instance will use + when communicating with peers. + This also controls which interface the traffic from the instance will exit. + Interface can be provided as Interface ID (e.g. `wan`, `lan`, `opt1`, etc.), + Interface physical name (e.g. `igb0`, `vtnet`, etc.) or Interface descriptive + name (e.g. `MY_LAN`, `MY_WAN`, etc.). + VIP can be provided as description, subnet or uniqid. + Failover group can be provided as group name. + + _Note: If you are using a failover group, the failover group must be configured to use the same_.' + type: string + local_port: + description: The port number upon which OpenVPN will listen for incoming connections from peers. + Firewall rules must allow traffic to this port and this port must be specified in the client configuration. + + The port for each server must be unique for each interface when using a standard + UDP or TCP Protocol choice and must be globally unique if using a multihome Protocol. + + Omitting this will default to next available port. + type: integer + example: 1194 + description: + description: Enter a description for this VPN instance, for reference. + type: string + example: 'My VPN' + custom_options: + description: Custom options may be added in using the Custom option box separated by a semicolon (;) + and the firewall will pass the custom directives to OpenVPN. + type: string + example: 'push "route 10.0.0.0 255.255.255.0";' + tls: + default: '' + description: TLS, or Transport Layer Security, can provide session + authentication and encryption to ensure the validity of peers and to protect the control channel. + Paste the TLS key. This is not necessary for mode `p2p_shared_key`. + Options are `""` or `true` for automatic TLS key generation, `false` to disable TLS + or a raw TLS key as a string. + Omitting disable TLS configuration. + type: string + example: '-----BEGIN OpenVPN Static key V1----- + db8701afd882d746be67f084bae68470 + 54a99ef3b61864cfe1864c6c02584335 + fe706df150250bf7e294b8c35911817c + 133c8d9b505573ebeb65259bc54c70ae + 88cc3163fd11a20f73d2c6fb3eea7cc2 + fdaefcde510486adc0acbb9481b3aef4 + 930db91806469218b2a4f92e71787cf4 + 635f5b3f773fefc28d492738d2648673 + 8fa9b23d41e5999f58c3f0004a59ecda + 119162e598764d9c8f6b99e7054f3ea4 + fdb4461913d2e3273a8a0db29332406a + 237bcfccca2315445ef809eaa933fa30 + a9a7510d2d167033edfd3580a824f3e1 + 1af57da6eee89e6318ec29c67da8a19d + 7c9d74c7afac6ee0f813a0278a6261d7 + a28e7bdbf1743527346bda359bc92fc9 + -----END OpenVPN Static key V1-----' + tls_type: + default: 'auth' + description: Enter TLS Key Usage Mode. This is not necessary for mode `p2p_shared_key`. + enum: + - auth + - crypt + type: string + tlsauth_keydir: + default: 'default' + description: The direction in which this VPN endpoint uses the TLS key. + The TLS Key Direction must be set to complementary values on the server and clients. + The default behavior uses 0 on server instances and 1 on client instances. + + Enter TLS keydir direction. This is not necessary for mode `p2p_shared_key`. + enum: + - default + - direction-0 + - direction-1 + - both-directions + type: string + shared_key: + default: '' + description: Enter Shared Key. This is only mandatory for mode `p2p_shared_key`. + Options are `""` for automatic shared key generation and a raw shared key. + type: string + example: '-----BEGIN OpenVPN Static key V1----- + db8701afd882d746be67f084bae68470 + 54a99ef3b61864cfe1864c6c02584335 + fe706df150250bf7e294b8c35911817c + 133c8d9b505573ebeb65259bc54c70ae + 88cc3163fd11a20f73d2c6fb3eea7cc2 + fdaefcde510486adc0acbb9481b3aef4 + 930db91806469218b2a4f92e71787cf4 + 635f5b3f773fefc28d492738d2648673 + 8fa9b23d41e5999f58c3f0004a59ecda + 119162e598764d9c8f6b99e7054f3ea4 + fdb4461913d2e3273a8a0db29332406a + 237bcfccca2315445ef809eaa933fa30 + a9a7510d2d167033edfd3580a824f3e1 + 1af57da6eee89e6318ec29c67da8a19d + 7c9d74c7afac6ee0f813a0278a6261d7 + a28e7bdbf1743527346bda359bc92fc9 + -----END OpenVPN Static key V1-----' + caref: + description: Specify the unique reference ID of the certificate signing authority. + type: string + example: 61c7fbce3a351 + crlref: + default: 'none' + description: This optional field is for the Certificate Revocation List (CRL) + this tunnel will use to check the validity of peer certificates. + + A CRL is a list of certificates signed by a CA which are no longer valid. + This could be due to a certificate being compromised or lost, such as from + a stolen laptop, spyware infection, etc. + + Specify the unique reference ID of the certificate. + type: string + example: 61c467cd2fd4f + certref: + description: The certificate used by the VPN instance to identify itself to peers. + This certificate must contain appropriate properties marking it as a server + certificate and not a user or client certificate. + + Specify the unique reference ID of the certificate revocation list. + type: string + example: 61c4121240420 + ocspcheck: + default: false + description: When set, OpenVPN will attempt to confirm client certificate validity + using Online Certificate Status Protocol (OCSP) against the site listed in the OCSP URL field. + type: boolean + ocspurl: + default: '' + description: Enter OCSP URL. This is only necessary for `ocspcheck`. + type: string + format: uri + dh_length: + default: '2048' + description: The Diffie-Hellman (DH) key exchange parameters are used for establishing a + secure communications channel. They may be regenerated at any time, and they are not + specific to an OpenVPN instance. When importing an existing OpenVPN configuration + these parameters do not need to be copied from the previous server nor does it require + generating new parameters. The length of the desired DH parameters may be chosen from + the drop-down box, either 1024, 2048, or 4096. There is an additional choice for + ECDH Only which disables traditional DH parameters and uses only ECDH. + enum: + - 1024 + - 2048 + - 3072 + - 4096 + - 6144 + - 7680 + - 8192 + - 15360 + - 16384 + - 'ECDH Only' + type: string + ecdh_curve: + default: 'none' + description: Configures a specific elliptic curve to use for Elliptic Curve Diffie-Hellman + key exchanges. This is only for use with ECDH TLS encryption. + enum: + - none + - prime256v1 + - secp384r1 + - secp521r1 + type: string + cert_depth: + default: 'one' + description: This option limits the valid length of a certificate chain. The default value + limits a chain to One (Client+Server). With that value, if an unauthorized intermediate + CA signs a certificate, certificates signed by the rogue intermediate would fail validation. + In cases when the certificate stricture requires chaining with intermediates, + raise this limit to accommodate the longest allowed chain. + enum: + - none + - one + - two + - three + - four + - five + type: string + strictusercn: + default: false + description: Controls whether the firewall will enforce a strict match between the username + supplied by the user and the Common Name of their user certificate when the firewall + authenticates a user. When enabled, authentication fails if the two values do not match. + + This prevents users from using their own credentials with the certificate from a different + user and vice versa. + + This is only necessary for mode `server_tls_user`. + Omitting will disable. + type: boolean + ncp_enable: + default: false + description: Enable Data Encryption Negotiation. + Disabling this feature is deprecated. + Omitting will enable. + type: boolean + data_ciphers: + default: 'AES-256-GCM,AES-128-GCM,CHACHA20-POLY1305' + description: These options define the cryptographic ciphers OpenVPN will use for this VPN. + Enter comma sepereted data encryption algorithms + (e.g. `AES-256-GCM,AES-128-GCM,CHACHA20-POLY1305`). + Data encryption algorithms will be truncated to 19. + type: string + data_ciphers_fallback: + default: 'AES-256-CBC' + description: Enter fallback data encryption algorithm. + type: string + digest: + default: 'SHA256' + description: Selects the message digest algorithm OpenVPN uses for HMAC authentication of + incoming packets. This is used for the data channel and if also for the control channel + when the tunnel uses a TLS key. The GUI default of SHA256 is a good balance of security and speed. + + When using AEAD ciphers such as AES-GCM, OpenVPN ignores this value for the data channel + since AEAD ciphers already perform authentication. Even with an AEAD cipher, OpenVPN still + uses this algorithm to authenticate the control channel when the tunnel uses a TLS key. + enum: + - BLAKE2b512 + - BLAKE2b256 + - MD4 + - MD5 + - MD5-SHA1 + - MDC2 + - RIPEMD160 + - SHA1 + - SHA224 + - SHA256 + - SHA3-224 + - SHA3-256 + - SHA3-384 + - SHA3-512 + - SHA384 + - SHA512 + - SHA512-224 + - SHA512-256 + - SHAKE128 + - SHAKE256 + - SM3 + - whirlpool + - none + type: string + engine: + default: 'none' + description: If available, this option controls which hardware cryptographic accelerator + will be used by OpenVPN. When left unspecified, OpenVPN will choose automatically based + on what is available in the operating system to accelerate ciphers OpenVPN wants to use. + + Some hardware acceleration, such as AES-NI, happens automatically in OpenVPN via OpenSSL + and cannot be enabled or disabled by this option. + + Enter Hardware crypto. Options are `none` and any enabled + hardware crypto. + type: string + tunnel_network: + default: '' + description: 'Virtual IPv4 network used for private communications + between this client and the server expressed using CIDR (e.g. + 10.0.8.5/24). + + With subnet topology, enter the client IP address and the subnet + mask must match the IPv4 Tunnel Network on the server. + + With net30 topology, the first network address of the /30 is assumed + to be the server address and the second network address will be + assigned to the client.' + type: string + tunnel_networkv6: + default: '' + description: 'Virtual IPv6 network used for private communications + between this client and the server expressed using prefix (e.g. + 2001:db9:1:1::100/64). + + Enter the client IPv6 address and prefix. The prefix must match + the IPv6 Tunnel Network prefix on the server.' + type: string + local_network: + default: '' + description: 'IPv4 server-side networks that will be accessible + from this particular client. Expressed as a comma-separated list + of one or more CIDR networks. + + NOTE: Networks do not need to be specified here if they have already + been defined on the main server configuration.' + type: string + local_networkv6: + default: '' + description: 'IPv6 server-side networks that will be accessible + from this particular client. Expressed as a comma-separated list + of one or more IP/PREFIX networks. + + NOTE: Networks do not need to be specified here if they have already + been defined on the main server configuration.' + type: string + remote_network: + default: '' + description: 'IPv4 client-side networks that will be routed to this + client specifically using iroute, so that a site-to-site VPN can + be established. Expressed as a comma-separated list of one or + more CIDR ranges. May be left blank if there are no client-side + networks to be routed. + + NOTE: Remember to add these subnets to the IPv4 Remote Networks + list on the corresponding OpenVPN server settings.' + type: string + remote_networkv6: + default: '' + description: 'IPv6 client-side networks that will be routed to this + client specifically using iroute, so that a site-to-site VPN can + be established. Expressed as a comma-separated list of one or + more IP/PREFIX networks. May be left blank if there are no client-side + networks to be routed. + + NOTE: Remember to add these subnets to the IPv6 Remote Networks + list on the corresponding OpenVPN server settings.' + type: string + redirect_gateway: + default: false + description: When a Redirect IPv4 Gateway option is selected the server + pushes a message to clients instructing them to forward all traffic for + that address family, including Internet traffic, over the VPN tunnel. + type: boolean + redirect_gateway6: + default: false + description: When a Redirect IPv6 Gateway option is selected the server + pushes a message to clients instructing them to forward all traffic for + that address family, including Internet traffic, over the VPN tunnel. + type: boolean + serverbridge_dhcp: + default: "" + description: When using `tap` mode for a remote access SSL/TLS VPN, + (`server_tls`, `server_user`, `server_tls_user`), additional options + to control bridging behavior in OpenVPN and client address assignment. + + When selected, OpenVPN passes through DHCP to the bridged. + In the most common scenario, this is LAN. + + Using this method connecting clients receive IP addresses from the + same DHCP pool used by clients on the LAN. + type: string + format: ipv4 + serverbridge_interface: + default: "none" + description: This setting indicates to OpenVPN which interface will be used for the bridge. + In most cases this is LAN. + This option controls which existing IP address and subnet mask are used by OpenVPN for the bridge. + Setting this to none will cause the Server Bridge DHCP (`serverbridge_dhcp_start`, `serverbridge_dhcp_end`) settings below to be ignored. + enum: + - wan + - lan + - none + type: string + serverbridge_routegateway: + description: Makes OpenVPN push the Bridge Interface IPv4 address to connecting + clients as a route gateway. + + When the IPv4 Tunnel Network in OpenVPN is empty for a bridged VPN, + connecting clients cannot automatically determine a server-side gateway for + use with routes pushed to clients by the server. + type: boolean + example: true + serverbridge_dhcp_start: + default: "" + description: Defines a DHCP range from which OpenVPN can assign addresses to clients. + If these settings are left blank, OpenVPN passes DHCP through to the bridge interface + and it ignores the interface setting above. + + When set, this range should be within the same subnet as the Bridge Interface + (`serverbridge_interface`) but it should not overlap an existing in-use portion + of the subnet, such as the current DHCP pool. + + This allows a range of IP addresses to be set aside for use only by OpenVPN clients so they may be contained within a portion of the internal network rather than consuming IP addresses from the existing DHCP pool. + type: string + format: ipv4 + serverbridge_dhcp_end: + default: "" + description: Defines a DHCP range from which OpenVPN can assign addresses to clients. + If these settings are left blank, OpenVPN passes DHCP through to the bridge interface + and it ignores the interface setting above. + + When set, this range should be within the same subnet as the Bridge Interface + (`serverbridge_interface`) but it should not overlap an existing in-use portion + of the subnet, such as the current DHCP pool. + + This allows a range of IP addresses to be set aside for use only by OpenVPN clients so they may be contained within a portion of the internal network rather than consuming IP addresses from the existing DHCP pool. + type: string + format: ipv4 + concurrent_connections: + description: Specifies the number of clients that may be simultaneously + connected to this OpenVPN server instance at any given time. + type: integer + example: '10' + allow_compression: + default: 'no' + description: Controls whether or not compression is allowed on the VPN, and how it is handled. + enum: + - asym + - no + - yes + type: string + compression: + default: 'none' + description: Compression to be used with this VPN instance. This is only + necessary for `allow_compression` set to `yes` or `asym`. + Compression can potentially dangerous and insecure. Deprecated. + Compress tunnel packets using the LZO algorithm. + enum: + - none + - stub + - stub-v2 + - lz4 + - lz4-v2 + - lzo + - noadapt + - adaptive + - yes + - no + type: string + compression_push: + default: false + description: Push the selected Compression setting to connecting clients. + This is only necessary for `allow_compression` set to `yes` or `asym`. + type: boolean + passtos: + default: false + description: When this option is enabled OpenVPN sets the Type-of-Service (TOS) + IP header value of tunnel packets to match the encapsulated packet value. + This may cause important traffic to be handled faster over the tunnel by + intermediate hops at the cost of minor information disclosure. + type: boolean + client2client: + default: false + description: This option controls whether or not connected clients are able to + communicate with one another. To allow this behavior, check the option. + When unchecked, clients can only send traffic to the server or destinations + beyond the server such as routed networks or the Internet. + type: boolean + duplicate_cn: + default: false + description: Controls whether or not OpenVPN will allow multiple connections + from the same user to work simultaneously. + This is only necessary for mode `p2p_shared_key`. + type: boolean + dynamic_ip: + default: false + description: For clients on Internet connections where the IP address changes frequently, + or mobile users who commonly move between different Internet connections, + check this option for more stable connectivity. Where the client IP address + is static or rarely changes, not using this option offers a small security improvement. + type: boolean + topology: + default: 'subnet' + description: Sets the method OpenVPN uses to allocate addresses for clients in a + client/server setup on tun device mode VPNs. The Topology option is relevant + only when supplying a virtual adapter IP address to clients using tun mode on IPv4. + Some clients may require this even for IPv6, such as OpenVPN Connect, though in + reality IPv6 always runs with a subnet topology even when IPv4 uses net30. + OpenVPN instances using tap mode always use subnet topology as well. + enum: + - subnet + - net30 + type: string + dns_domain: + default: '' + description: Configures a default domain name which clients will append to DNS requests. + This can be helpful to ensure name resolution works properly for hosts on the local + network where DNS name resolution is used. + type: string + example: example.com + dns_servers: + default: '' + description: Comma-separated list of no more than 4 DNS server IP + addresses (i.e. 8.8.8.8). Any more than the first four are ignored. + This list is provided to the clent. + type: string + example: "8.8.8.8, 8.8.4.4, 8.8.3.3, 8.8.2.2" + push_blockoutsidedns: + default: false + description: Make Windows 10 Clients Block access to DNS servers except + across OpenVPN while connected, forcing clients to use only VPN DNS servers. + type: boolean + push_register_dns: + default: false + description: Run "net stop dnscache", "net start dnscache", + "ipconfig /flushdns" and "ipconfig /registerdns" on connection initiation. + type: boolean + inactive_seconds: + default: 300 + description: The amount of time, in seconds, which a client can be inactive before + OpenVPN disconnects it for inactivity. OpenVPN bases activity on the last incoming + or outgoing data channel packet, not control channel packets. + + `0` disables this feature. + type: integer + ping_method: + default: 'keepalive' + description: The Ping Method controls OpenVPN monitoring of peers through the + control channel and how it deals with unresponsive peers. + enum: + - keepalive + - ping + type: string + keepalive_interval: + default: 10 + description: Enter keepalive interval. + type: integer + keepalive_timeout: + default: 60 + description: Enter keepalive timeout. + type: integer + ping_seconds: + default: 10 + description: Enter ping seconds. This is only necessary for `ping` method. + type: integer + ping_action_seconds: + default: 60 + description: Enter ping action seconds. This is only necessary for `ping` method. + type: integer + ping_push: + default: false + description: Push ping to VPN client. This is only necessary for `ping` method. + type: boolean + ping_action_push: + default: false + description: Push ping-restart/ping-exit to VPN client. + This is only necessary for `ping` method. + type: boolean + ping_action: + default: 'ping_restart' + description: Exit or restart OpenVPN after timeout from remote. + This is only necessary for `ping` method. + enum: + - ping_restart + - ping_exit + type: string + username_as_common_name: + default: false + description: Controls whether or not OpenVPN will use the username given by the + client in place of the certificate common name (CN) for purposes such as determining + Client Specific Overrides. This is only relevant when user authentication is enabled. + This is typically the best practice, but not a requirement. + + This is only necessary for mode `server_user` or `server_tls_user`. + type: boolean + udp_fast_io: + default: false + description: Controls whether or not OpenVPN will use fast I/O operations with UDP writes + to its tun or tap device. This behavior optimizes the packet write event loop, + improving CPU efficiency by 5% to 10%. Experimental. + type: boolean + exit_notify: + default: 'none' + description: Controls whether or not OpenVPN will send an explicit exit notification to + connected UDP clients or peers when restarting or shutting down. This notification + allows peers to immediately disconnect rather than wait for a timeout. + This is only relevant to UDP modes as TCP natively supports closing connections. + enum: + - once + - twice + - none + - '' + type: string + sndrcvbuf: + default: 'default' + description: Configures a Send and Receive Buffer size for OpenVPN. + The default buffer size can be too small in many cases, depending on hardware + and network uplink speeds. Finding the best buffer size can take experimentation. + To test the best value for a site, start at 512KiB and test higher and lower + values until testing results in peak performance. + enum: + - 64KiB + - 128KiB + - 256KiB + - 512KiB + - 1MiB + - 2MiB + - default + - none + type: string + ntp_servers: + default: '' + description: Comma-separated list of no more than 2 NTP server IP + addresses. Any more than the first two are ignored. + type: string + example: "192.168.56.101, 192.168.56.102" + netbios_enable: + default: false + description: Enable NetBIOS over TCP/IP. If this option is not set, + all NetBIOS-over-TCP/IP options (including WINS) will be disabled. + type: boolean + netbios_node_type: + description: 'Possible options: b (broadcasts), p (point-to-point + name queries to a WINS server), m (broadcast then query name server), + and h (query name server, then broadcast). This parameter takes + a single letter either b, p, m, or h. Any other string will result + in this option being set to none.' + enum: + - b + - p + - m + - h + type: string + example: b + netbios_scope: + description: NetBIOS Scope ID provides an extended naming service + for NetBIOS over TCP/IP. The NetBIOS scope ID isolates NetBIOS + traffic on a single network to only those nodes with the same + NetBIOS scope ID. + type: string + example: '5' + create_gw: + default: 'both' + description: If you assign a virtual interface to this OpenVPN server, + this setting controls which gateway types will be created. + enum: + - v4only + - v6only + - both + type: string + verbosity_level: + default: '3' + description: Configures the amount of detail OpenVPN will log for this instance, + which is useful for troubleshooting problems. Higher numbers will result in + higher amounts of detail in the log. + During normal operation the default selection is ideal. + Level `3` is recommended for a good summary of what's happening without + being swamped by output. Maximum level `11`. + The `default` setting is equivalent to level `1`. + enum: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - 'default' + type: string + wins_servers: + default: '' + description: Comma-separated list of no more than 2 WINS server + IP addresses. Any more than the first two are ignored. + type: string + example: "192.168.56.103, 192.168.56.104" + disable: + default: false + description: Disable this override without removing it. + type: boolean + required: + - vpnid + type: object + responses: + 200: + $ref: '#/components/responses/Success' + 401: + $ref: '#/components/responses/AuthenticationFailed' + summary: Update OpenVPN Server + tags: + - Services > OpenVPN > Server /api/v1/services/openvpn/csc: delete: parameters: @@ -9764,4 +11273,3 @@ tags: - name: Services > DDNS - name: Services > WOL - name: Diagnostics > Command Prompt - diff --git a/tests/test_api_v1_services_openvpn_server.py b/tests/test_api_v1_services_openvpn_server.py new file mode 100644 index 000000000..914034911 --- /dev/null +++ b/tests/test_api_v1_services_openvpn_server.py @@ -0,0 +1,611 @@ +from operator import truediv +import e2e_test_framework +import base64 + +class APIE2ETestOpenVPNServer(e2e_test_framework.APIE2ETest): + uri = "/api/v1/services/openvpn/server" + tls_key_text = "-----BEGIN OpenVPN Static key V1----- \ +db8701afd882d746be67f084bae68470 \ +54a99ef3b61864cfe1864c6c02584335 \ +fe706df150250bf7e294b8c35911817c \ +133c8d9b505573ebeb65259bc54c70ae \ +88cc3163fd11a20f73d2c6fb3eea7cc2 \ +fdaefcde510486adc0acbb9481b3aef4 \ +930db91806469218b2a4f92e71787cf4 \ +635f5b3f773fefc28d492738d2648673 \ +8fa9b23d41e5999f58c3f0004a59ecda \ +119162e598764d9c8f6b99e7054f3ea4 \ +fdb4461913d2e3273a8a0db29332406a \ +237bcfccca2315445ef809eaa933fa30 \ +a9a7510d2d167033edfd3580a824f3e1 \ +1af57da6eee89e6318ec29c67da8a19d \ +7c9d74c7afac6ee0f813a0278a6261d7 \ +a28e7bdbf1743527346bda359bc92fc9 \ +-----END OpenVPN Static key V1----- \ +" + get_tests = [{"name": "Read all OpenVPN Server instances"}] + post_tests = [ + { + "name": "OpenVPN Server No Certificate Authority found", + "status": 400, + "return": 2144, + "payload": {} + }, + { + "name": "Create RSA internal CA", + "uri": "/api/v1/system/ca", + "no_caref": True, # Prevents the overriden post_post() method from auto-adding the created CA ref ID + "payload": { + "method": "internal", + "descr": "INTERNAL_CA_TEST", + "trust": True, + "keytype": "RSA", + "keylen": 2048, + "digest_alg": "sha256", + "dn_commonname": "internal-ca-e2e-test.example.com" + }, + }, + { + "name": "OpenVPN Server No Server Certificate found", + "status": 400, + "return": 2145, + "payload": {} + }, + { + "name": "Create internal certificate with RSA key", + "uri": "/api/v1/system/certificate", + "no_certref": True, # Prevents the overriden post_post() method from auto-adding the created Certificate ref ID + "payload": { + "method": "internal", + "descr": "INTERNAL_CERT_RSA", + "keytype": "RSA", + "keylen": 2048, + "digest_alg": "sha256", + "lifetime": 3650, + "dn_commonname": "internal-cert-e2e-test.example.com", + "dn_country": "US", + "dn_city": "Salt Lake City", + "dn_state": "Utah", + "dn_organization": "Test Company", + "dn_organizationalunit": "IT", + "type": "server" + } + }, + { + "name": "Create OpenVPN Server", + "status": 200, + "return": 0, + "payload": { + "protocol": "udp4", + "description": "TEST_Create_OpenVPN_SERVER", + "tls": "", + "tls_type": "crypt", + "tlsauth_keydir": "both-directions", + "ecdh_curve": "prime256v1", + "ncp_enable": True, + "data_ciphers": "AES-128-GCM,CHACHA20-POLY1305", + "data_ciphers_fallback": "AES-256-CBC", + "digest": "SHA512", + "dh_length": "4096", + "tunnel_network": "1.2.3.4/24", + "concurrent_connections": 15, + "custom_options": 'push "route 10.0.0.0 255.255.255.0";' + } + }, + { + "name": "Create Interface (ovpns1)", + "uri": "/api/v1/interface", + "no_caref": True, # Prevents the overriden post_post() method from auto-adding the created CA ref ID + "no_certref": True, # Prevents the overriden post_post() method from auto-adding the created Certificate ref ID + # "resp_time": 30, + "payload": { + "if": "ovpns1", + "descr": "TEST_INTERFACE_OVPNS1", + } + }, + { + "name": "Unknown OpenVPN Server Mode", + "status": 400, + "return": 2104, + "payload": { + "mode": "INVALID_MODE" + } + }, + { + "name": "Unknown OpenVPN Server Protocol", + "status": 400, + "return": 2105, + "payload": { + "protocol": "INVALID_PROTOCOL" + } + }, + { + "name": "Unknown OpenVPN Server Device mode", + "status": 400, + "return": 2106, + "payload": { + "dev_mode": "INVALID_DEV_MODE" + } + }, + { + "name": "Unknown OpenVPN Server Interface", + "status": 400, + "return": 2107, + "payload": { + "interface": "INVALID_INTERFACE" + } + }, + { + "name": "Invalid or taken OpenVPN Server Local port (allowed range 1-65535)", + "status": 500, + "return": 2108, + "payload": { + "local_port": "99999" + } + }, + { + "name": "Unknown OpenVPN Server TLS Key Usage Mode", + "status": 400, + "return": 2109, + "payload": { + "tls": tls_key_text, + "tls_type": "INVALID_TLS_TYPE" + } + }, + { + "name": "Unknown OpenVPN Server TLS keydir direction", + "status": 400, + "return": 2110, + "payload": { + "tls": tls_key_text, + "tlsauth_keydir": "INVALID_TLS_KEYDIR" + } + }, + { + "name": "Unknown OpenVPN Server TLS DH Parameter Length", + "status": 400, + "return": 2111, + "payload": { + "dh_length": "INVALID_DH_LENGTH" + } + }, + { + "name": "Unknown OpenVPN Server ECDH Curve", + "status": 400, + "return": 2112, + "payload": { + "ecdh_curve": "INVALID_ECDH_CURVE" + } + }, + { + "name": "Unknown OpenVPN Server Certificate Depth", + "status": 400, + "return": 2113, + "payload": { + "cert_depth": "INVALID_CERT_DEPTH" + } + }, + { + "name": "Unknown OpenVPN Server Fallback Data Encryption Algorithm", + "status": 400, + "return": 2114, + "payload": { + "data_ciphers_fallback": "INVALID_DATA_CIPHERS_FALLBACK" + } + }, + { + "name": "Unknown OpenVPN Server Auth Digest Algorithm", + "status": 400, + "return": 2115, + "payload": { + "digest": "INVALID_DIGEST" + } + }, + { + "name": "Unknown OpenVPN Server Hardware Crypto Engine", + "status": 400, + "return": 2116, + "payload": { + "engine": "INVALID_ENGINE" + } + }, + { + "name": "Unknown or already in use OpenVPN Server Tunnel Network", + "status": 400, + "return": 2117, + "payload": { + "tunnel_network": "INVALID_TUNNEL_NETWORK" + } + }, + { + "name": "Unknown OpenVPN Server Remote network(s)", + "status": 400, + "return": 2118, + "payload": { + "remote_network": "INVALID_REMOTE_NETWORK" + } + }, + { + "name": "Unknown OpenVPN Server Local network(s)", + "status": 400, + "return": 2119, + "payload": { + "local_network": "INVALID_LOCAL_NETWORK" + } + }, + { + "name": "Unknown OpenVPN Server Allow Compression", + "status": 400, + "return": 2120, + "payload": { + "allow_compression": "INVALID_ALLOW_COMPRESSION" + } + }, + { + "name": "Unknown OpenVPN Server Compression", + "status": 400, + "return": 2121, + "payload": { + "allow_compression": "yes", + "compression": "INVALID_COMPRESSION" + } + }, + { + "name": "Unknown OpenVPN Server Topology", + "status": 400, + "return": 2122, + "payload": { + "dev_mode": "tun", + "topology": "INVALID_TOPOLOGY" + } + }, + { + "name": "Unknown OpenVPN Server Ping Method", + "status": 400, + "return": 2123, + "payload": { + "ping_method": "INVALID_PING_METHOD" + } + }, + { + "name": "Unknown OpenVPN Server Keepalive Interval", + "status": 400, + "return": 2124, + "payload": { + "ping_method": "keepalive", + "keepalive_interval": "INVALID_KEEPALIVE_INTERVAL" + } + }, + { + "name": "Unknown OpenVPN Server Keepalive Timeout", + "status": 400, + "return": 2125, + "payload": { + "ping_method": "keepalive", + "keepalive_timeout": "INVALID_KEEPALIVE_TIMEOUT" + } + }, + { + "name": "Unknown OpenVPN Server Ping Seconds", + "status": 400, + "return": 2126, + "payload": { + "ping_method": "ping", + "ping_seconds": "INVALID_PING_SECONDS" + } + }, + { + "name": "Unknown OpenVPN Server Ping restart or exit seconds", + "status": 400, + "return": 2127, + "payload": { + "ping_method": "ping", + "ping_action_seconds": "INVALID_PING_ACTION_SECONDS" + } + }, + { + "name": "Unknown OpenVPN Server Ping restart or exit", + "status": 400, + "return": 2128, + "payload": { + "ping_method": "ping", + "ping_action": "INVALID_PING_ACTION" + } + }, + { + "name": "Unknown OpenVPN Server Exit Notify", + "status": 400, + "return": 2129, + "payload": { + "exit_notify": "INVALID_EXIT_NOTIFY" + } + }, + { + "name": "Unknown OpenVPN Server Send/Receive Buffer", + "status": 400, + "return": 2130, + "payload": { + "sndrcvbuf": "INVALID_SNDRCVBUF" + } + }, + { + "name": "Unknown OpenVPN Server Gateway Creation", + "status": 400, + "return": 2131, + "payload": { + "create_gw": "INVALID_CREATE_GW" + } + }, + { + "name": "Unknown OpenVPN Server Verbosity Level", + "status": 400, + "return": 2132, + "payload": { + "verbosity_level": "INVALID_VERBOSITY_LEVEL" + } + }, + { + "name": "Unknown OpenVPN Server Concurrent Connections", + "status": 400, + "return": 2133, + "payload": { + "concurrent_connections": "INVALID_CONCURRENT_CONNECTIONS" + } + }, + { + "name": "Unknown OpenVPN Server Inactive Time", + "status": 400, + "return": 2134, + "payload": { + "inactive_seconds": "INVALID_INACTIVE_SECONDS" + } + }, + { + "name": "Missing OpenVPN Server Certificate", + "status": 400, + "return": 2135, + "no_certref": True, # Prevents the overriden post_post() method from auto-adding the created Certificate ref ID + "payload": {} + }, + { + "name": "Missing OpenVPN Server Certificate Authority", + "status": 400, + "return": 2136, + "no_caref": True, # Prevents the overriden post_post() method from auto-adding the created CA ref ID + "payload": {} + }, + { + "name": "Unknown OpenVPN Server Peer Certificate Revocation List", + "status": 400, + "return": 2137, + "payload": { + "crlref": "INVALID_CRLREF" + } + }, + { + "name": "Unknown OpenVPN Server Data Encryption Algorithms", + "status": 400, + "return": 2138, + "payload": { + "data_ciphers": "INVALID_DATA_ENCRYP_ALGO" + } + }, + { + "name": "Missing OpenVPN Server 'shared_key'. This parameter is needed for server mode 'p2p_shared_key'", + "status": 400, + "return": 2141, + "no_certref": True, # Prevents the overriden post_post() method from auto-adding the created Certificate ref ID + "no_caref": True, # Prevents the overriden post_post() method from auto-adding the created CA ref ID + "payload": { + "mode": "p2p_shared_key" + } + }, + { + "name": "Unknown OpenVPN Server Certificate Authority", + "status": 400, + "return": 2142, + "no_caref": True, # Prevents the overriden post_post() method from auto-adding the created CA ref ID + "payload": { + "caref": "INVALID_CA_REF", + } + }, + { + "name": "Unknown OpenVPN Server Certificate", + "status": 400, + "return": 2143, + "no_certref": True, # Prevents the overriden post_post() method from auto-adding the created Certificate ref ID + "payload": { + "certref": "INVALID_CERT_REF", + } + }, + { + "name": "Unknown OpenVPN Server Bridge Interface", + "status": 400, + "return": 2146, + "payload": { + "mode": "server_tls", + "dev_mode": "tap", + "serverbridge_dhcp": True, + "serverbridge_interface": "INVALID_BRIDGE_INTERFACE" + } + }, + { + "name": "Unknown OpenVPN Server Bridge Route Gateway requires a valid Bridge Interface", + "status": 400, + "return": 2147, + "payload": { + "mode": "server_tls", + "dev_mode": "tap", + "serverbridge_dhcp": True, + "serverbridge_routegateway": True, + "serverbridge_interface": "none" + } + }, + { + "name": "Unknown OpenVPN Server Bridge DHCP Start and End must both be empty, or defined, or The Server Bridge DHCP range is invalid (start higher than end).", + "status": 400, + "return": 2148, + "payload": { + "mode": "server_tls", + "dev_mode": "tap", + "serverbridge_dhcp": True, + "serverbridge_dhcp_start": "INVALID_DHCP_START" + } + }, + { + "name": "OpenVPN Server The field 'NTP Server' must contain a valid IP address.", + "status": 400, + "return": 2149, + "payload": { + "mode": "server_tls", + "ntp_servers": "INVALID_NTP_SERVER" + } + }, + { + "name": "OpenVPN Server The field 'DNS Server' must contain a valid IP address.", + "status": 400, + "return": 2150, + "payload": { + "mode": "server_tls", + "dns_servers": "INVALID_DNS_SERVER" + } + }, + { + "name": "OpenVPN Server The field 'WINS Server' must contain a valid IP address.", + "status": 400, + "return": 2151, + "payload": { + "mode": "server_tls", + "wins_servers": "INVALID_WINS_SERVER" + } + }, + ] + put_tests = [ + { + "name": "OpenVPN Server Update 1", + "status": 200, + "return": 0, + "no_certref": True, # Prevents the overriden post_post() method from auto-adding the created Certificate ref ID + "no_caref": True, # Prevents the overriden post_post() method from auto-adding the created CA ref ID + "payload": { + "description": "TEST_Update_OpenVPN_SERVER_1", + "mode": "p2p_shared_key", + "shared_key": "" + } + }, + { + "name": "OpenVPN Server Update 2", + "status": 200, + "return": 0, + "payload": { + "description": "TEST_Update_OpenVPN_SERVER_2", + "mode": "server_tls_user", + "tls": "false", + "dev_mode": "tap", + "local_network": "4.3.2.1/24", + "compression_push": True, + "client2client": True, + "dynamic_ip": True, + "exit_notify": "twice", + "netbios_enable": True, + "netbios_ntype": "m", + "wins_servers": "10.0.0.10,10.0.0.20", + "serverbridge_dhcp": True, + "strictusercn": True, + "username_as_common_name": True, + } + } + ] + delete_tests = [ + { + "name": "OpenVPN Server cannot delete an OpenVPN instance while the interface is assigned. Remove the interface assignment first.", + "status": 400, + "return": 2152, + "payload": {} + }, + { + "name": "Delete Interface (ovpns1)", + "uri": "/api/v1/interface", + "status": 200, + "return": 0, + "payload": {} + }, + { + "name": "Delete OpenVPN Server Instance", + "status": 200, + "return": 0, + "payload": {}, + "resp_time": 10 + }, # vpnid gets populated by post_post() method + { + "name": "Delete Certificate", + "uri": "/api/v1/system/certificate", + "status": 200, + "return": 0, + "payload": {"descr": "INTERNAL_CERT_RSA"} + }, + { + "name": "Delete CA certificate", + "uri": "/api/v1/system/ca", + "status": 200, + "return": 0, + "payload": {"descr": "INTERNAL_CA_TEST"} + }, + { + "name": "Unknown OpenVPN Server 'vpnid'", + "status": 404, + "return": 2139, + "payload": { + "vpnid": "INVALID_VPNID" + } + }, + { + "name": "Missing OpenVPN Server 'vpnid'. This parameter is needed to identify the server to modify/delete", + "status": 400, + "return": 2140, + "payload": {} + }, + ] + + # Override our PRE/POST methods + def post_post(self): + if len(self.post_responses) == 2: + # Variables + counter = 0 + for test in self.post_tests: + # Assign the required refid created in the POST request to the POST/PUT payloads + if "payload" in test.keys() and "no_caref" not in test.keys(): + self.post_tests[counter]["payload"]["caref"] = self.post_responses[1]["data"]["refid"] + self.put_tests[1]["payload"]["caref"] = self.post_responses[1]["data"]["refid"] + # self.put_tests[1]["payload"]["caref"] = self.post_responses[1]["data"]["refid"] + counter = counter + 1 + + if len(self.post_responses) == 4: + # Variables + counter = 0 + for test in self.post_tests: + # Assign the required refid created in the POST request to the POST/PUT payloads + if "payload" in test.keys() and "no_certref" not in test.keys(): + self.post_tests[counter]["payload"]["certref"] = self.post_responses[3]["data"]["refid"] + self.put_tests[1]["payload"]["certref"] = self.post_responses[3]["data"]["refid"] + counter = counter + 1 + + if len(self.post_responses) == 5: + # Variables + # counter = 0 + for test in self.post_tests: + # Assign the required vpnid created in the POST request to the DELETE/PUT payloads + self.delete_tests[0]["payload"]["vpnid"] = self.post_responses[4]["data"]["vpnid"] + self.delete_tests[2]["payload"]["vpnid"] = self.post_responses[4]["data"]["vpnid"] + self.put_tests[0]["payload"]["vpnid"] = self.post_responses[4]["data"]["vpnid"] + self.put_tests[1]["payload"]["vpnid"] = self.post_responses[4]["data"]["vpnid"] + # counter = counter + 1 + + if len(self.post_responses) == 6: + # Variables + # counter = 0 + for test in self.post_tests: + # Assign the required vpnid created in the POST request to the DELETE payloads + self.delete_tests[1]["payload"]["if"] = self.post_responses[5]["data"]["if"] + +APIE2ETestOpenVPNServer() \ No newline at end of file