diff --git a/README.md b/README.md index 8a4fd06f..df67af3e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -![](https://i.imgur.com/DpgvLIO.png) -[![Release 2.7.1](https://img.shields.io/badge/release-v2.7.1-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Financial Contributors on Open Collective](https://opencollective.com/raspap/all/badge.svg?label=financial+contributors)](https://opencollective.com/raspap) ![https://travis-ci.com/github/raspap/raspap-webgui/](https://api.travis-ci.org/RaspAP/raspap-webgui.svg) [![Crowdin](https://badges.crowdin.net/raspap/localized.svg)](https://crowdin.com/project/raspap) [![Twitter URL](https://img.shields.io/twitter/url?label=%40RaspAP&logoColor=%23d8224c&url=https%3A%2F%2Ftwitter.com%2Frasp_ap)](https://twitter.com/rasp_ap) [![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/RaspAP?style=social)](https://www.reddit.com/r/RaspAP/) +![](https://i.imgur.com/TCJKWT4.png) +[![Release 2.7.2](https://img.shields.io/badge/release-v2.7.2-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Financial Contributors on Open Collective](https://opencollective.com/raspap/all/badge.svg?label=financial+contributors)](https://opencollective.com/raspap) ![https://travis-ci.com/github/raspap/raspap-webgui/](https://api.travis-ci.org/RaspAP/raspap-webgui.svg) [![Crowdin](https://badges.crowdin.net/raspap/localized.svg)](https://crowdin.com/project/raspap) [![Twitter URL](https://img.shields.io/twitter/url?label=%40RaspAP&logoColor=%23d8224c&url=https%3A%2F%2Ftwitter.com%2Frasp_ap)](https://twitter.com/rasp_ap) [![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/RaspAP?style=social)](https://www.reddit.com/r/RaspAP/) Welcome to **RaspAP Insiders**. You, the members of the Insiders community, support the sponsorware release model, which means that new features are first exclusively released to sponsors as part of Insiders. Read on for details about how this strategy works—and *thank you* for joining us on this journey. @@ -25,16 +25,16 @@ As part of the initial rollout of Insiders, all previous one-time backers of Ras > ℹ️ **Important**: If you're [sponsoring](https://github.com/sponsors/RaspAP) RaspAP through a GitHub organization, please send a short email to [sponsors@raspap.com](mailto:sponsors@raspap.com) with the name of your organization and the account that should be added as a collaborator. [2](#footnote-2) ## Exclusive features -When backers were asked which feature they'd most like to see added to RaspAP, the ability to manage multiple OpenVPN client configurations topped the list of requests. Therefore, we're adding this as the first feature exclusive to insiders. +The following features are currently available exclusively to sponsors. A tangible side benefit of sponsorship is that Insiders are able to help steer future development of RaspAP. This is done through your Insiders access to discussions, feature requests, issues and pull requests in the private GitHub repository. ✅ Manage OpenVPN client configs ✅ OpenVPN service logging ✅ Night mode toggle ✅ Restrict network to static clients -⚙️ WireGuard support (in progress) +✅ WireGuard support ⚙️ Traffic shaping (in progress) -Look for the list above to grow as we add more exlcusive features. Have an idea or suggestion for a future enhancement? Start or join an [Insiders discussion](https://github.com/orgs/RaspAP/teams/insiders/discussions) and let us know! +Look for the list above to grow as we add more exlcusive features. Have an idea or suggestion for a future enhancement? Start or join an [Insiders discussion](https://github.com/RaspAP/raspap-insiders/discussions) and let us know! ## Funding targets Following is a list of funding targets. When a funding target is reached, the features that are tied to it are merged back into RaspAP and released to the public for general availability. @@ -44,7 +44,7 @@ Following is a list of funding targets. When a funding target is reached, the fe ✅ OpenVPN service logging ✅ Night mode toggle ✅ Restrict network to static clients -⚙️ WireGuard support (in progress) +✅ WireGuard support ⚙️ Traffic shaping (in progress) ## Frequently asked questions diff --git a/ajax/networking/get_wgcfg.php b/ajax/networking/get_wgcfg.php new file mode 100644 index 00000000..6a9d771d --- /dev/null +++ b/ajax/networking/get_wgcfg.php @@ -0,0 +1,9 @@ + $pubkey_tmp", $return); + $wgdata['pubkey'] = str_replace("\n",'',file_get_contents($pubkey_tmp)); + exec("sudo mv $privkey_tmp $privkey", $return); + exec("sudo mv $pubkey_tmp $pubkey", $return); + + echo json_encode($wgdata); +} diff --git a/app/css/hackernews.css b/app/css/hackernews.css index 1dbb2e97..f2c68cbf 100644 --- a/app/css/hackernews.css +++ b/app/css/hackernews.css @@ -75,6 +75,9 @@ h5.card-title { font-family: Verdana, Geneva, sans-serif; } +.sidebar-light hr.sidebar-divider { + padding-top: 0.5rem; +} ul.nav-tabs, .nav-tabs .nav-link { background-color: #f6f6ef; @@ -154,6 +157,7 @@ ul.nav-tabs, .nav-tabs .nav-link { .info-item-xs { font-size: 0.7rem; margin-left: 0.3rem; + line-height: 1.5em; } .info-item-wifi { @@ -190,6 +194,10 @@ ul.nav-tabs, .nav-tabs .nav-link { } } +.fas.fa-circle { + font-size: 0.5rem; +} + .logoutput { width:100%; height:300px; diff --git a/app/img/wg-qr-code.php b/app/img/wg-qr-code.php new file mode 100644 index 00000000..7a66e6fc --- /dev/null +++ b/app/img/wg-qr-code.php @@ -0,0 +1,28 @@ + Array.from({length: (end - start)}, (v, k) => k + start); diff --git a/config/config.php b/config/config.php index f0516cfc..8003a320 100755 --- a/config/config.php +++ b/config/config.php @@ -21,6 +21,8 @@ define('RASPI_WPA_CTRL_INTERFACE', '/var/run/wpa_supplicant'); define('RASPI_OPENVPN_CLIENT_CONFIG', '/etc/openvpn/client/client.conf'); define('RASPI_OPENVPN_CLIENT_LOGIN', '/etc/openvpn/client/login.conf'); define('RASPI_OPENVPN_SERVER_CONFIG', '/etc/openvpn/server/server.conf'); +define('RASPI_WIREGUARD_PATH', '/etc/wireguard/'); +define('RASPI_WIREGUARD_CONFIG', RASPI_WIREGUARD_PATH.'wg0.conf'); define('RASPI_TORPROXY_CONFIG', '/etc/tor/torrc'); define('RASPI_LIGHTTPD_CONFIG', '/etc/lighttpd/lighttpd.conf'); define('RASPI_ACCESS_CHECK_IP', '1.1.1.1'); @@ -39,6 +41,7 @@ define('RASPI_NETWORK_ENABLED', true); define('RASPI_DHCP_ENABLED', true); define('RASPI_ADBLOCK_ENABLED', false); define('RASPI_OPENVPN_ENABLED', false); +define('RASPI_WIREGUARD_ENABLED', false); define('RASPI_TORPROXY_ENABLED', false); define('RASPI_CONFAUTH_ENABLED', true); define('RASPI_CHANGETHEME_ENABLED', true); diff --git a/config/defaults.json b/config/defaults.json index 94f80da0..faab15b6 100644 --- a/config/defaults.json +++ b/config/defaults.json @@ -33,6 +33,22 @@ "uap0": { "dhcp-range": [ "192.168.50.50,192.168.50.150,12h" ] } + }, + "wireguard": { + "server": { + "Address": [ "10.8.2.1/24" ], + "ListenPort": [ "51820" ], + "DNS": [ "9.9.9.9" ], + "PostUp": [ "iptables -A FORWARD -i wlan0 -o wg0 -j ACCEPT; iptables -A FORWARD -i wg0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT; iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE" ], + "PostDown": [ "iptables -D FORWARD -i wlan0 -o wg0 -j ACCEPT; iptables -D FORWARD -i wg0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT; iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE" ] + }, + "peer": { + "Address": [ "10.8.1.2/24" ], + "Endpoint": [ "10.8.2.1:51820" ], + "ListenPort": [ "21841" ], + "AllowedIPs": ["10.8.2.0/24"], + "PersistentKeepalive": [ "15" ] + } } } diff --git a/dist/raspap/css/fonts/RaspAP.eot b/dist/raspap/css/fonts/RaspAP.eot new file mode 100755 index 00000000..d77690f6 Binary files /dev/null and b/dist/raspap/css/fonts/RaspAP.eot differ diff --git a/dist/raspap/css/fonts/RaspAP.svg b/dist/raspap/css/fonts/RaspAP.svg new file mode 100755 index 00000000..27920e40 --- /dev/null +++ b/dist/raspap/css/fonts/RaspAP.svg @@ -0,0 +1,12 @@ + + + +Generated by IcoMoon + + + + + + + + \ No newline at end of file diff --git a/dist/raspap/css/fonts/RaspAP.ttf b/dist/raspap/css/fonts/RaspAP.ttf new file mode 100755 index 00000000..11221442 Binary files /dev/null and b/dist/raspap/css/fonts/RaspAP.ttf differ diff --git a/dist/raspap/css/fonts/RaspAP.woff b/dist/raspap/css/fonts/RaspAP.woff new file mode 100755 index 00000000..875f0253 Binary files /dev/null and b/dist/raspap/css/fonts/RaspAP.woff differ diff --git a/dist/raspap/css/style.css b/dist/raspap/css/style.css new file mode 100644 index 00000000..45305ca1 --- /dev/null +++ b/dist/raspap/css/style.css @@ -0,0 +1,54 @@ + /*! + * RaspAP-Brands Brand Icons - https://raspap.com + * License - https://github.com/billz/RaspAP-Brands-webgui/blob/master/LICENSE + */ +@font-face { + font-family: 'RaspAP'; + src: url('fonts/RaspAP.eot?e76qs3'); + src: url('fonts/RaspAP.eot?e76qs3#iefix') format('embedded-opentype'), + url('fonts/RaspAP.ttf?e76qs3') format('truetype'), + url('fonts/RaspAP.woff?e76qs3') format('woff'), + url('fonts/RaspAP.svg?e76qs3#RaspAP') format('svg'); + font-weight: normal; + font-style: normal; + font-display: block; +} + +[class^="ra-"], [class*=" ra-"] { + /* use !important to prevent issues with browser extensions that change ..webfonts */ + font-family: 'RaspAP' !important; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.ra-wireguard:before { + font-size: 1.2rem; + content: "\e900"; + color: #d1d3e2; + vertical-align: middle; +} + +.card-header .ra-wireguard:before { + color: #fff; +} + +.sidebar .nav-item.active .nav-link +span.ra-wireguard:before { + color: #6e707e; +} + +.ra-raspap:before { + font-size: 4.35rem; + content: "\e901"; + color: #d8224c; + margin-left: 0.1em; +} + diff --git a/includes/defaults.php b/includes/defaults.php index 3cde7d64..34e08378 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -6,7 +6,7 @@ if (!defined('RASPI_CONFIG')) { $defaults = [ 'RASPI_BRAND_TEXT' => 'RaspAP', - 'RASPI_VERSION' => '2.7.1', + 'RASPI_VERSION' => '2.7.2', 'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json', 'RASPI_ADMIN_DETAILS' => RASPI_CONFIG.'/raspap.auth', 'RASPI_WIFI_AP_INTERFACE' => 'wlan0', @@ -26,6 +26,8 @@ $defaults = [ 'RASPI_OPENVPN_CLIENT_CONFIG' => '/etc/openvpn/client/client.conf', 'RASPI_OPENVPN_CLIENT_LOGIN' => '/etc/openvpn/client/login.conf', 'RASPI_OPENVPN_SERVER_CONFIG' => '/etc/openvpn/server/server.conf', + 'RASPI_WIREGUARD_PATH' => '/etc/wireguard/', + 'RASPI_WIREGUARD_CONFIG' => RASPI_WIREGUARD_PATH.'wg0.conf', 'RASPI_TORPROXY_CONFIG' => '/etc/tor/torrc', 'RASPI_LIGHTTPD_CONFIG' => '/etc/lighttpd/lighttpd.conf', 'RASPI_ACCESS_CHECK_IP' => '1.1.1.1', @@ -42,6 +44,7 @@ $defaults = [ 'RASPI_DHCP_ENABLED' => true, 'RASPI_ADBLOCK_ENABLED' => false, 'RASPI_OPENVPN_ENABLED' => false, + 'RASPI_WIREGUARD_ENABLED' => false, 'RASPI_TORPROXY_ENABLED' => false, 'RASPI_CONFAUTH_ENABLED' => true, 'RASPI_CHANGETHEME_ENABLED' => true, diff --git a/includes/functions.php b/includes/functions.php index 4d03fe8b..2f1c502b 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -715,6 +715,33 @@ function getBridgedState() return $arrHostapdConf['BridgedEnable']; } +/** + * Validates the format of a CIDR notation string + * + * @param string $cidr + * @return bool + */ +function validateCidr($cidr) +{ + $parts = explode('/', $cidr); + if(count($parts) != 2) { + return false; + } + $ip = $parts[0]; + $netmask = intval($parts[1]); + + if($netmask < 0) { + return false; + } + if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + return $netmask <= 32; + } + if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + return $netmask <= 128; + } + return false; +} + // Validates a host or FQDN function validate_host($host) { return preg_match('/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i', $host); @@ -740,3 +767,11 @@ function preg_only_match($pat,$haystack) { } return $match; } + +// Sanitizes a string for QR encoding +// @param string $str +// @return string +function qr_encode($str) +{ + return preg_replace('/(?addMessage('Attempting to start WireGuard', 'info'); + exec('sudo /bin/systemctl start wg-quick@wg0', $return); + foreach ($return as $line) { + $status->addMessage($line, 'info'); + } + } elseif (isset($_POST['stopwg'])) { + $status->addMessage('Attempting to stop WireGuard', 'info'); + exec('sudo /bin/systemctl stop wg-quick@wg0', $return); + foreach ($return as $line) { + $status->addMessage($line, 'info'); + } + } + } + + // fetch wg config + exec('sudo cat '. RASPI_WIREGUARD_CONFIG, $return); + $conf = ParseConfig($return); + $wg_srvpubkey = exec('sudo cat '. RASPI_WIREGUARD_PATH .'wg-server-public.key', $return); + $wg_srvport = ($conf['ListenPort'] == '') ? getDefaultNetValue('wireguard','server','ListenPort') : $conf['ListenPort']; + $wg_srvipaddress = ($conf['Address'] == '') ? getDefaultNetValue('wireguard','server','Address') : $conf['Address']; + $wg_srvdns = ($conf['DNS'] == '') ? getDefaultNetValue('wireguard','server','DNS') : $conf['DNS']; + $wg_peerpubkey = exec('sudo cat '. RASPI_WIREGUARD_PATH .'wg-peer-public.key', $return); + if (sizeof($conf) >0) { + $wg_senabled = true; + } + + // todo: iterate multiple peer configs + exec('sudo cat '. RASPI_WIREGUARD_PATH.'client.conf', $preturn); + $conf = ParseConfig($preturn); + $wg_pipaddress = ($conf['Address'] == '') ? getDefaultNetValue('wireguard','peer','Address') : $conf['Address']; + $wg_plistenport = ($conf['ListenPort'] == '') ? getDefaultNetValue('wireguard','peer','ListenPort') : $conf['ListenPort']; + $wg_pendpoint = ($conf['Endpoint'] == '') ? getDefaultNetValue('wireguard','peer','Endpoint') : $conf['Endpoint']; + $wg_pallowedips = ($conf['AllowedIPs'] == '') ? getDefaultNetValue('wireguard','peer','AllowedIPs') : $conf['AllowedIPs']; + $wg_pkeepalive = ($conf['PersistentKeepalive'] == '') ? getDefaultNetValue('wireguard','peer','PersistentKeepalive') : $conf['PersistentKeepalive']; + if (sizeof($conf) >0) { + $wg_penabled = true; + } + + // fetch service status + exec('pidof wg-crypt-wg0 | wc -l', $wgstatus); + $serviceStatus = $wgstatus[0] == 0 ? "down" : "up"; + $wg_state = ($wgstatus[0] > 0); + + echo renderTemplate( + "wireguard", compact( + "status", + "wg_state", + "serviceStatus", + "wg_log", + "peer_id", + "wg_srvpubkey", + "wg_srvport", + "wg_srvipaddress", + "wg_srvdns", + "wg_senabled", + "wg_penabled", + "wg_pipaddress", + "wg_plistenport", + "wg_peerpubkey", + "wg_pendpoint", + "wg_pallowedips", + "wg_pkeepalive" + ) + ); +} + +/** + * Validate user input, save wireguard configuration + * + * @param object $status + * @return boolean + */ +function SaveWireGuardConfig($status) +{ + // Set defaults + $good_input = true; + $peer_id = 1; + // Validate server input + if ($_POST['wg_senabled'] == 1) { + if (isset($_POST['wg_srvport'])) { + if (strlen($_POST['wg_srvport']) > 5 || !is_numeric($_POST['wg_srvport'])) { + $status->addMessage('Invalid value for server local port', 'danger'); + $good_input = false; + } + } + if (isset($_POST['wg_plistenport'])) { + if (strlen($_POST['wg_plistenport']) > 5 || !is_numeric($_POST['wg_plistenport'])) { + $status->addMessage('Invalid value for peer local port', 'danger'); + $good_input = false; + } + } + if (isset($_POST['wg_srvipaddress'])) { + if (!validateCidr($_POST['wg_srvipaddress'])) { + $status->addMessage('Invalid value for server IP address', 'danger'); + $good_input = false; + } + } + if (isset($_POST['wg_srvdns'])) { + if (!filter_var($_POST['wg_srvdns'],FILTER_VALIDATE_IP)) { + $status->addMessage('Invalid value for DNS', 'danger'); + $good_input = false; + } + } + } + // Validate peer input + if ($_POST['wg_penabled'] == 1) { + if (isset($_POST['wg_pipaddress'])) { + if (!validateCidr($_POST['wg_pipaddress'])) { + $status->addMessage('Invalid value for peer IP address', 'danger'); + $good_input = false; + } + } + if (isset($_POST['wg_pendpoint']) && strlen(trim($_POST['wg_pendpoint']) >0 )) { + $wg_pendpoint_seg = substr($_POST['wg_pendpoint'],0,strpos($_POST['wg_pendpoint'],':')); + if (!filter_var($wg_pendpoint_seg,FILTER_VALIDATE_IP)) { + $status->addMessage('Invalid value for endpoint address', 'danger'); + $good_input = false; + } + } + if (isset($_POST['wg_pallowedips']) && strlen(trim($_POST['wg_pallowedips']) >0)) { + if (!validateCidr($_POST['wg_pallowedips'])) { + $status->addMessage('Invalid value for allowed IPs', 'danger'); + $good_input = false; + } + } + if (isset($_POST['wg_pkeepalive']) && strlen(trim($_POST['wg_pkeepalive']) >0 )) { + if (strlen($_POST['wg_pkeepalive']) > 4 || !is_numeric($_POST['wg_pkeepalive'])) { + $status->addMessage('Invalid value for persistent keepalive', 'danger'); + $good_input = false; + } + } + } + // Save settings + if ($good_input) { + // server (wg0.conf) + if ($_POST['wg_senabled'] == 1) { + // fetch server private key from filesytem + $wg_srvprivkey = exec('sudo cat '. RASPI_WIREGUARD_PATH .'wg-server-private.key', $return); + $config[] = '[Interface]'; + $config[] = 'Address = '.$_POST['wg_srvipaddress']; + $config[] = 'ListenPort = '.$_POST['wg_srvport']; + $config[] = 'DNS = '.$_POST['wg_srvdns']; + $config[] = 'PrivateKey = '.$wg_srvprivkey; + $config[] = 'PostUp = '.getDefaultNetValue('wireguard','server','PostUp'); + $config[] = 'PostDown = '.getDefaultNetValue('wireguard','server','PostDown'); + $config[] = ''; + $config[] = '[Peer]'; + $config[] = 'PublicKey = '.$_POST['wg-peer']; + $config[] = 'AllowedIPs = '.$_POST['wg_pallowedips']; + if ($_POST['wg_pkeepalive'] !== '') { + $config[] = 'PersistentKeepalive = '.trim($_POST['wg_pkeepalive']); + } + $config[] = ''; + $config = join(PHP_EOL, $config); + + file_put_contents("/tmp/wgdata", $config); + system('sudo cp /tmp/wgdata '.RASPI_WIREGUARD_CONFIG, $return); + } else { + # remove selected conf + keys + system('sudo rm '. RASPI_WIREGUARD_PATH .'wg-server-private.key', $return); + system('sudo rm '. RASPI_WIREGUARD_PATH .'wg-server-public.key', $return); + system('sudo rm '. RASPI_WIREGUARD_CONFIG, $return); + } + // client1 (client.conf) + if ($_POST['wg_penabled'] == 1) { + // fetch peer private key from filesystem + $wg_peerprivkey = exec('sudo cat '. RASPI_WIREGUARD_PATH .'wg-peer-private.key', $return); + $config = []; + $config[] = '[Interface]'; + $config[] = 'Address = '.trim($_POST['wg_pipaddress']); + $config[] = 'PrivateKey = '.$wg_peerprivkey; + $config[] = 'ListenPort = '.$_POST['wg_plistenport']; + $config[] = ''; + $config[] = '[Peer]'; + $config[] = 'PublicKey = '.$_POST['wg-server']; + $config[] = 'AllowedIPs = '.$_POST['wg_pallowedips']; + $config[] = 'Endpoint = '.$_POST['wg_pendpoint']; + if ($_POST['wg_pkeepalive'] !== '') { + $config[] = 'PersistentKeepalive = '.trim($_POST['wg_pkeepalive']); + } + $config[] = ''; + $config = join(PHP_EOL, $config); + + file_put_contents("/tmp/wgdata", $config); + system('sudo cp /tmp/wgdata '.RASPI_WIREGUARD_PATH.'client.conf', $return); + } else { + # remove selected conf + keys + system('sudo rm '. RASPI_WIREGUARD_PATH .'wg-peer-private.key', $return); + system('sudo rm '. RASPI_WIREGUARD_PATH .'wg-peer-public.key', $return); + system('sudo rm '. RASPI_WIREGUARD_PATH.'client.conf', $return); + } + + // handle log option + if ($_POST['wg_log'] == "1") { + exec("sudo /bin/systemctl status wg-quick@wg0 | sudo tee /tmp/wireguard.log > /dev/null"); + } + foreach ($return as $line) { + $status->addMessage($line, 'info'); + } + if ($return == 0) { + $status->addMessage('WireGuard configuration updated successfully', 'success'); + } else { + $status->addMessage('WireGuard configuration failed to be updated', 'danger'); + } + } +} + diff --git a/index.php b/index.php index 806d7241..065dd2c5 100755 --- a/index.php +++ b/index.php @@ -14,7 +14,7 @@ * @author Lawrence Yau * @author Bill Zimmerman * @license GNU General Public License, version 3 (GPL-3.0) - * @version 2.7.1 + * @version 2.7.2 * @link https://github.com/raspap/raspap-insiders/ * @link https://raspap.com/ * @see http://sirlagz.net/2013/02/08/raspap-webgui/ @@ -45,6 +45,7 @@ require_once 'includes/themes.php'; require_once 'includes/data_usage.php'; require_once 'includes/about.php'; require_once 'includes/openvpn.php'; +require_once 'includes/wireguard.php'; require_once 'includes/torproxy.php'; $config = getConfig(); @@ -82,6 +83,9 @@ $bridgedEnabled = getBridgedState(); + + + @@ -161,6 +165,11 @@ $bridgedEnabled = getBridgedState(); + + + @@ -259,6 +268,9 @@ $bridgedEnabled = getBridgedState(); case "/openvpn_conf": DisplayOpenVPNConfig(); break; + case "/wg_conf": + DisplayWireGuardConfig(); + break; case "/torproxy_conf": DisplayTorProxyConfig(); break; diff --git a/installers/common.sh b/installers/common.sh index dd44ee90..f30ac6fa 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -28,7 +28,12 @@ readonly raspap_network="$raspap_dir/networking/" readonly rulesv4="/etc/iptables/rules.v4" readonly notracking_url="https://raw.githubusercontent.com/notracking/hosts-blocklists/master/" webroot_dir="/var/www/html" -git_source_url="https://github.com/$repo" # $repo from install.raspap.com + +if [ "$insiders" == 1 ]; then + repo="RaspAP/raspap-insiders" + branch=${RASPAP_INSIDERS_LATEST} +fi +git_source_url="https://github.com/$repo" # NOTE: all the below functions are overloadable for system-specific installs function _install_raspap() { @@ -51,6 +56,7 @@ function _install_raspap() { _prompt_install_adblock _prompt_install_openvpn _install_features + _prompt_install_wireguard _patch_system_files _install_complete } @@ -93,7 +99,7 @@ function _config_installation() { fi echo "${opt[1]} lighttpd directory: ${webroot_dir}" if [ "$upgrade" == 1 ]; then - echo "This will upgrade your existing install to version ${RASPAP_LATEST}" + echo "This will upgrade your existing install to version ${RASPAP_RELEASE}" echo "Your configuration will NOT be changed" fi echo -n "Complete ${opt[2]} with these values? [Y/n]: " @@ -334,6 +340,49 @@ function _prompt_install_openvpn() { fi } +# Prompt to install WireGuard +function _prompt_install_wireguard() { + if [ "$insiders" == 1 ]; then + _install_log "Configure WireGuard support" + echo -n "Install WireGuard and enable VPN tunnel configuration? [Y/n]: " + if [ "$assume_yes" == 0 ]; then + read answer < /dev/tty + if [ "$answer" != "${answer#[Nn]}" ]; then + echo -e + else + _install_wireguard + fi + elif [ "$wg_option" == 1 ]; then + _install_wireguard + else + echo "(Skipped)" + fi + fi +} + +# Install Wireguard from the Debian unstable distro +function _install_wireguard() { + _install_log "Configure WireGuard support" + if [ "$OS" == "Raspbian" ]; then + echo "Installing raspberrypi-kernel-headers" + sudo apt-get install $apt_option raspberrypi-kernel-headers || _install_status 1 "Unable to install raspberrypi-kernel-headers" + fi + echo "Installing WireGuard from Debian unstable distro" + echo "Adding Debian distro" + echo "deb http://deb.debian.org/debian/ unstable main" | sudo tee --append /etc/apt/sources.list.d/unstable.list || _install_status 1 "Unable to append to sources.list" + sudo apt-get install dirmngr || _install_status 1 "Unable to install dirmngr" + echo "Adding Debian distro keys" + sudo wget -q -O - https://ftp-master.debian.org/keys/archive-key-$(lsb_release -sr).asc | sudo apt-key add - || _install_status 1 "Unable to add keys" + printf 'Package: *\nPin: release a=unstable\nPin-Priority: 150\n' | sudo tee --append /etc/apt/preferences.d/limit-unstable || _install_status 1 "Unable to append to preferences.d" + echo "Installing WireGuard" + sudo apt-get update && sudo apt-get install $apt_option wireguard || _install_status 1 "Unable to install wireguard" + echo "Enabling wg-quick@wg0" + sudo systemctl enable wg-quick@wg0 || _install_status 1 "Failed to enable wg-quick service" + echo "Enabling WireGuard management option" + sudo sed -i "s/\('RASPI_WIREGUARD_ENABLED', \)false/\1true/g" "$webroot_dir/includes/config.php" || _install_status 1 "Unable to modify config.php" + _install_status 0 +} + # Install openvpn and enable client configuration option function _install_openvpn() { _install_log "Installing OpenVPN and enabling client configuration" diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index 4c3b5b68..39ea7697 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -41,8 +41,11 @@ www-data ALL=(ALL) NOPASSWD:/etc/raspap/openvpn/configauth.sh www-data ALL=(ALL) NOPASSWD:/etc/raspap/openvpn/openvpnlog.sh www-data ALL=(ALL) NOPASSWD:/bin/chmod o+r /tmp/hostapd.log www-data ALL=(ALL) NOPASSWD:/bin/chmod o+r /tmp/dnsmasq.log +www-data ALL=(ALL) NOPASSWD:/bin/chmod o+r /tmp/wireguard.log www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/dnsmasqdata /etc/dnsmasq.d/090_adblock.conf www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/dnsmasq_custom /etc/raspap/adblock/custom.txt +www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/wgdata /etc/wireguard/*.conf +www-data ALL=(ALL) NOPASSWD:/bin/mv /tmp/wg-*.key /etc/wireguard/wg-*.key www-data ALL=(ALL) NOPASSWD:/etc/raspap/adblock/update_blocklist.sh www-data ALL=(ALL) NOPASSWD:/usr/bin/socat - /dev/ttyUSB[0-9] www-data ALL=(ALL) NOPASSWD:/usr/local/sbin/onoff_huawei_hilink.sh * @@ -50,3 +53,10 @@ www-data ALL=(ALL) NOPASSWD:/bin/sed -i * /etc/wvdial.conf www-data ALL=(ALL) NOPASSWD:/bin/sed -i * /etc/udev/rules.d/80-raspap-net-devices.rules www-data ALL=(ALL) NOPASSWD:/usr/bin/tee -a /etc/udev/rules.d/80-raspap-net-devices.rules www-data ALL=(ALL) NOPASSWD:/usr/local/sbin/switchClientState.sh * +www-data ALL=(ALL) NOPASSWD:/usr/bin/tee /tmp/wireguard.log +www-data ALL=(ALL) NOPASSWD:/bin/systemctl * wg-quick@wg0 +www-data ALL=(ALL) NOPASSWD:/usr/bin/wg +www-data ALL=(ALL) NOPASSWD:/bin/cat /etc/wireguard/*.conf +www-data ALL=(ALL) NOPASSWD:/bin/cat /etc/wireguard/wg-*.key +www-data ALL=(ALL) NOPASSWD:/bin/rm /etc/wireguard/*.conf +www-data ALL=(ALL) NOPASSWD:/bin/rm /etc/wireguard/wg-*.key \ No newline at end of file diff --git a/installers/raspbian.sh b/installers/raspbian.sh index 0e3c8611..52878c85 100755 --- a/installers/raspbian.sh +++ b/installers/raspbian.sh @@ -15,11 +15,11 @@ # -c, --cert, --certficate Installs mkcert and generates an SSL certificate for lighttpd # -o, --openvpn Used with -y, --yes, sets OpenVPN install option (0=no install) # -a, --adblock Used with -y, --yes, sets Adblock install option (0=no install) -# -r, --repo, --repository Overrides the default GitHub repo (raspap/raspap-webgui) +# -r, --repo, --repository Overrides the default GitHub repo (RaspAP/raspap-webgui) # -b, --branch Overrides the default git branch (master) -# -t, --token Token to access a private repository +# -t, --token Specify a GitHub token to access a private repository # -u, --upgrade Upgrades an existing installation to the latest release version -# -i, --insiders Installs from the Insiders Edition (raspap/raspap-insiders) +# -i, --insiders Installs from the Insiders Edition (RaspAP/raspap-insiders) # -v, --version Outputs release info and exits # -h, --help Outputs usage notes and exits # @@ -37,7 +37,7 @@ set -eo pipefail function _main() { # set defaults - repo="raspap/raspap-webgui" # override with -r, --repo option + repo="RaspAP/raspap-webgui" # override with -r, --repo option _parse_params "$@" _setup_colors _log_output @@ -45,11 +45,12 @@ function _main() { } function _parse_params() { - # default flag values + # default option values assume_yes=0 upgrade=0 ovpn_option=1 adblock_option=1 + insiders=0 acctoken="" while :; do @@ -84,7 +85,11 @@ function _parse_params() { upgrade=1 ;; -i|--insiders) - repo="raspap/raspap-insiders" + insiders=1 + ;; + -t|--token) + acctoken="$2" + shift ;; -t|--token) acctoken="$2" @@ -132,10 +137,11 @@ OPTIONS: -c, --cert, --certificate Installs an SSL certificate for lighttpd -o, --openvpn Used with -y, --yes, sets OpenVPN install option (0=no install) -a, --adblock Used with -y, --yes, sets Adblock install option (0=no install) --r, --repo, --repository Overrides the default GitHub repo (raspap/raspap-webgui) +-r, --repo, --repository Overrides the default GitHub repo (RaspAP/raspap-webgui) -b, --branch Overrides the default git branch (latest release) +-t, --token Specify a GitHub token to access a private repository -u, --upgrade Upgrades an existing installation to the latest release version --i, --insiders Installs from the Insiders Edition (raspap/raspap-insiders) +-i, --insiders Installs from the Insiders Edition (RaspAP/raspap-insiders) -v, --version Outputs release info and exits -h, --help Outputs usage notes and exits @@ -158,7 +164,7 @@ EOF function _version() { _get_release - echo -e "RaspAP v${RASPAP_LATEST} - Simple wireless AP setup & management for Debian-based devices" + echo -e "RaspAP v${RASPAP_RELEASE} - Simple wireless AP setup & management for Debian-based devices" exit } @@ -172,20 +178,19 @@ function _display_welcome() { echo -e " 88 88 88. .88 88 88. .88 88 88 88" echo -e " dP dP 88888P8 88888P 88Y888P 88 88 dP" echo -e " 88" - echo -e " dP version ${RASPAP_LATEST}" + echo -e " dP version ${RASPAP_RELEASE}" echo -e "${ANSI_GREEN}" echo -e "The Quick Installer will guide you through a few easy steps${ANSI_RESET}\n\n" } -# Fetch latest release from GitHub API +# Fetch latest release from GitHub or RaspAP Installer API function _get_release() { - if [ "$repo" == "raspap/raspap-insiders" ]; then - readonly RASPAP_LATEST="Insiders" - if [ -z ${branch} ]; then - branch="master" - fi + readonly RASPAP_LATEST=$(curl -s "https://api.github.com/repos/$repo/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")' ) + if [ "$insiders" == 1 ]; then + readonly RASPAP_INSIDERS_LATEST=$(curl -s "https://install.raspap.com/repos/RaspAP/raspap-insiders/releases/latest/" | grep -Po '"tag_name": "\K.*?(?=")' ) + readonly RASPAP_RELEASE="${RASPAP_INSIDERS_LATEST} Insiders" else - readonly RASPAP_LATEST=$(curl -s "https://api.github.com/repos/$repo/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")' ) + readonly RASPAP_RELEASE="${RASPAP_LATEST}" fi } @@ -230,6 +235,12 @@ function _load_installer() { branch=$RASPAP_LATEST fi + # add optional auth token header if defined with -t, --token option + header=() + if [[ ! -z "$acctoken" ]]; then + header=(--header "Authorization: token $acctoken") + fi + UPDATE_URL="https://raw.githubusercontent.com/$repo/$branch/" header=() if [[ ! -z "$acctoken" ]]; then @@ -242,7 +253,7 @@ function _load_installer() { _install_certificate || _install_status 1 "Unable to install certificate" else source="common" - wget "${header[@]}" -q ${UPDATE_URL}installers/${source}.sh -O /tmp/raspap_${source}.sh + wget "${header[@]}" -q ${UPDATE_URL}installers/${source}.sh -O /tmp/raspap_${source}.sh source /tmp/raspap_${source}.sh && rm -f /tmp/raspap_${source}.sh _install_raspap || _install_status 1 "Unable to install RaspAP" fi diff --git a/locale/en_US/LC_MESSAGES/messages.mo b/locale/en_US/LC_MESSAGES/messages.mo index 451dd153..d9fec98a 100644 Binary files a/locale/en_US/LC_MESSAGES/messages.mo and b/locale/en_US/LC_MESSAGES/messages.mo differ diff --git a/locale/en_US/LC_MESSAGES/messages.po b/locale/en_US/LC_MESSAGES/messages.po index 26677ce7..74627378 100644 --- a/locale/en_US/LC_MESSAGES/messages.po +++ b/locale/en_US/LC_MESSAGES/messages.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: 1.2.1\n" "Report-Msgid-Bugs-To: Bill Zimmerman \n" "POT-Creation-Date: 2017-10-19 08:56+0000\n" -"PO-Revision-Date: 2020-03-29 00:05+0000\n" +"PO-Revision-Date: 2021-03-08 09:00+0000\n" "Last-Translator: Bill Zimmerman \n" "Language-Team: \n" "Language: en_US\n" @@ -725,12 +725,6 @@ msgstr "Cancel" msgid "Enable this option to log openvpn activity." msgstr "Enable this option to log openvpn activity." -msgid "Cancel" -msgstr "Cancel" - -msgid "Cancel" -msgstr "Cancel" - #: includes/torproxy.php msgid "TOR is not running" msgstr "TOR is not running" @@ -856,3 +850,89 @@ msgstr "Invalid custom IP address found on line " msgid "Invalid custom host found on line " msgstr "Invalid custom host found on line " +#: includes/wireguard.php + +msgid "Tunnel settings" +msgstr "Tunnel settings" + +msgid "Enable server" +msgstr "Enable server" + +msgid "Enable this option to encrypt traffic by creating a tunnel between RaspAP and configured peers." +msgstr "Enable this option to encrypt traffic by creating a tunnel between RaspAP and configured peers." + +msgid "This option adds wg0.conf to the WireGuard configuration." +msgstr "This option adds wg0.conf to the WireGuard configuration." + +msgid "Local public key" +msgstr "Local public key" + +msgid "Local Port" +msgstr "Local Port" + +msgid "IP Address" +msgstr "IP Address" + +msgid "DNS" +msgstr "DNS" + +msgid "Peer" +msgstr "Peer" + +msgid "Enable peer" +msgstr "Enable peer" + +msgid "Enable this option to encrypt traffic by creating a tunnel between RaspAP and this peer." +msgstr "Enable this option to encrypt traffic by creating a tunnel between RaspAP and this peer." + +msgid "This option adds client.conf to the WireGuard configuration." +msgstr "This option adds client.conf to the WireGuard configuration." + +msgid "Peer public key" +msgstr "Peer public key" + +msgid "Endpoint address" +msgstr "Endpoint address" + +msgid "Allowed IPs" +msgstr "Allowed IPs" + +msgid "Persistent keepalive" +msgstr "Persistent keepalive" + +msgid "Display WireGuard status" +msgstr "Display WireGuard status" + +msgid "Enable this option to display an updated WireGuard status." +msgstr "Enable this option to display an updated WireGuard status." + +msgid "Scan this QR code with your client to connect to this tunnel" +msgstr "Scan this QR code with your client to connect to this tunnel" + +msgid "or download the client.conf file to your device." +msgstr "or download the client.conf file to your device." + +msgid "Download" +msgstr "Download" + +msgid "Start WireGuard" +msgstr "Start WireGuard" + +msgid "Stop WireGuard" +msgstr "Stop WireGuard" + +msgid "Information provided by wireguard" +msgstr "Information provided by wireguard" + +msgid "Attempting to start WireGuard" +msgstr "Attempting to start WireGuard" + +msgid "Attempting to stop WireGuard" +msgstr "Attempting to stop WireGuard" + +msgid "WireGuard configuration updated successfully" +msgstr "WireGuard configuration updated successfully" + +msgid "WireGuard configuration failed to be updated" +msgstr "WireGuard configuration failed to be updated" + diff --git a/locale/it_IT/LC_MESSAGES/messages.mo b/locale/it_IT/LC_MESSAGES/messages.mo index b5846ff2..b894df8c 100644 Binary files a/locale/it_IT/LC_MESSAGES/messages.mo and b/locale/it_IT/LC_MESSAGES/messages.mo differ diff --git a/locale/it_IT/LC_MESSAGES/messages.po b/locale/it_IT/LC_MESSAGES/messages.po index 328f5ea7..63163cf1 100644 --- a/locale/it_IT/LC_MESSAGES/messages.po +++ b/locale/it_IT/LC_MESSAGES/messages.po @@ -3,8 +3,8 @@ msgstr "" "Project-Id-Version: raspap\n" "Report-Msgid-Bugs-To: Bill Zimmerman \n" "POT-Creation-Date: 2017-10-19 08:56+0000\n" -"PO-Revision-Date: 2021-01-21 10:40\n" -"Last-Translator: Luca Sasdelli\n" +"PO-Revision-Date: 2021-03-20 14:38\n" +"Last-Translator: Ioma Taani (iomataani)\n" "Language-Team: Italian\n" "Language: it_IT\n" "MIME-Version: 1.0\n" @@ -361,6 +361,24 @@ msgstr "Registra le richieste DHCP" msgid "Log DNS queries" msgstr "Registra query DNS" +msgid "Restrict access" +msgstr "Limita l'accesso" + +msgid "Limit network access to static clients" +msgstr "Limita l'accesso di rete ai client statici" + +msgid "Enable this option if you want RaspAP to ignore any clients which are not specified in the static leases list." +msgstr "Abilita questa opzione se vuoi che RaspAP ignori qualsiasi client non sono specificato nell'elenco dei lease statici." + +msgid "This option adds dhcp-ignore to the dnsmasq configuration." +msgstr "Questa opzione aggiunge dhcp-ignore alla configurazione di dnsmasq." + +msgid "Clients with a particular hardware MAC address can always be allocated the same IP address." +msgstr "I client con un particolare indirizzo MAC possono essere assegnati sempre allo stesso indirizzo IP." + +msgid "This option adds dhcp-host entries to the dnsmasq configuration." +msgstr "Questa opzione aggiunge dhcp-host alla configurazione dnsmasq." + #: includes/hostapd.php msgid "Basic" msgstr "Base" @@ -676,6 +694,36 @@ msgstr "Tentativo di avviare openvpn in corso" msgid "Attempting to stop openvpn" msgstr "Tentativo di arrestare openvpn in corso" +msgid "Configurations" +msgstr "Configurazioni" + +msgid "Currently available OpenVPN client configurations are displayed below." +msgstr "Le configurazioni client OpenVPN attualmente disponibili sono visualizzate di seguito." + +msgid "Activating a configuraton will restart the openvpn-client service." +msgstr "Attivando la configurazione si riavvierà il servizio openvpn-client." + +msgid "Delete OpenVPN client" +msgstr "Elimina client OpenVPN" + +msgid "Delete client configuration? This cannot be undone." +msgstr "Eliminare la configurazione del client? Questa operazione non può essere annullata." + +msgid "Activate OpenVPN client" +msgstr "Attiva client OpenVPN" + +msgid "Activate client configuration? This will restart the openvpn-client service." +msgstr "Attivare la configurazione del client? Questo riavvierà il servizio openvpn-client." + +msgid "Activate" +msgstr "Attiva" + +msgid "Cancel" +msgstr "Annulla" + +msgid "Enable this option to log openvpn activity." +msgstr "Abilita questa opzione per registrare l'attività di openvpn." + #: includes/torproxy.php msgid "TOR is not running" msgstr "TOR non è in esecuzione" @@ -799,3 +847,87 @@ msgstr "Host personalizzato non valido trovato sulla riga " msgid "Invalid custom host found on line " msgstr "Host personalizzato non valido trovato sulla riga " +msgid "Tunnel settings" +msgstr "Impostazioni del tunnel" + +msgid "Enable server" +msgstr "Abilita il server" + +msgid "Enable this option to encrypt traffic by creating a tunnel between RaspAP and configured peers." +msgstr "Abilita questa opzione per cifrare il traffico creando un tunnel tra RaspAP e peer configurati." + +msgid "This option adds wg0.conf to the WireGuard configuration." +msgstr "Questa opzione aggiunge wg0.conf alla configurazione di WireGuard." + +msgid "Local public key" +msgstr "Chiave pubblica locale" + +msgid "Local Port" +msgstr "Porta locale" + +msgid "IP Address" +msgstr "Indirizzo IP" + +msgid "DNS" +msgstr "DNS" + +msgid "Peer" +msgstr "Peer" + +msgid "Enable peer" +msgstr "Abilita peer" + +msgid "Enable this option to encrypt traffic by creating a tunnel between RaspAP and this peer." +msgstr "Abilita questa opzione per cifrare il traffico creando un tunnel tra RaspAP e questo peer." + +msgid "This option adds client.conf to the WireGuard configuration." +msgstr "Questa opzione aggiunge client.conf alla configurazione di WireGuard." + +msgid "Peer public key" +msgstr "Chiave pubblica del peer" + +msgid "Endpoint address" +msgstr "Indirizzo di endpoint" + +msgid "Allowed IPs" +msgstr "IP consentiti" + +msgid "Persistent keepalive" +msgstr "Keepalive permanente" + +msgid "Display WireGuard status" +msgstr "Mostra lo stato di WireGuard" + +msgid "Enable this option to display an updated WireGuard status." +msgstr "Abilita questa opzione per visualizzare lo stato aggiornato di WireGuard." + +msgid "Scan this QR code with your client to connect to this tunnel" +msgstr "Scansiona questo codice QR con il tuo client per connetterti a questo tunnel" + +msgid "or download the client.conf file to your device." +msgstr "o scarica il file client.conf sul tuo dispositivo." + +msgid "Download" +msgstr "Scarica" + +msgid "Start WireGuard" +msgstr "Avvia WireGuard" + +msgid "Stop WireGuard" +msgstr "Ferma WireGuard" + +msgid "Information provided by wireguard" +msgstr "Informazioni fornite da wireguard" + +msgid "Attempting to start WireGuard" +msgstr "Tentativo di avviare WireGuard in corso" + +msgid "Attempting to stop WireGuard" +msgstr "Tentativo di arrestare WireGuard in corso" + +msgid "WireGuard configuration updated successfully" +msgstr "Configurazione WireGuard aggiornata con successo" + +msgid "WireGuard configuration failed to be updated" +msgstr "Impossibile aggiornare la configurazione di WireGuard" + diff --git a/templates/wg/general.php b/templates/wg/general.php new file mode 100644 index 00000000..06d09811 --- /dev/null +++ b/templates/wg/general.php @@ -0,0 +1,55 @@ + +
+
+
+

+
+
+ aria-describedby="server-description"> + +
+

+ + wg0.conf to the WireGuard configuration.") ?> +

+
+ +
+
+ +
+
+ +
+ + +
+
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+
+ + diff --git a/templates/wg/logging.php b/templates/wg/logging.php new file mode 100644 index 00000000..c9cb4185 --- /dev/null +++ b/templates/wg/logging.php @@ -0,0 +1,19 @@ + +
+
+
+

+
+ aria-describedby="wg_log"> + +
+

+ '.htmlspecialchars($log, ENT_QUOTES).''; + ?> +
+
+
+ diff --git a/templates/wg/peers.php b/templates/wg/peers.php new file mode 100644 index 00000000..d4347575 --- /dev/null +++ b/templates/wg/peers.php @@ -0,0 +1,82 @@ + +
+
+
+

+
+ +
+ aria-describedby="endpoint-description"> + +
+

+ + client.conf to the WireGuard configuration.") ?> +

+
+ +
+
+ +
+
+ +
+ + +
+
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+
+ +
+
+ + RaspAP Wifi QR code +
+ + client.conf file to your device."); ?> +
+ + +
+
+ +
+
+ diff --git a/templates/wireguard.php b/templates/wireguard.php new file mode 100644 index 00000000..ee28dfa1 --- /dev/null +++ b/templates/wireguard.php @@ -0,0 +1,53 @@ + + + "> + + "> + + "> + + + + +
+
+
+
+
+
+ +
+
+ +
+
+
+
+ showMessages(); ?> +
+ + + + + +
+ + + +
+ + +
+
+ +
+
+
+