From 5028007b7aa5b6b0b771c9ea86452b7a573745fd Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 20 Apr 2020 10:14:12 +0100 Subject: [PATCH 001/300] Add wireguard install option --- installers/common.sh | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/installers/common.sh b/installers/common.sh index a64e8d1cc..e52d62d71 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -235,17 +235,35 @@ function _install_adblock() { _install_status 0 } -# Prompt to install openvpn -function _prompt_install_openvpn() { - _install_log "Configure OpenVPN support" - echo -n "Install OpenVPN and enable client configuration? [Y/n]: " +# Prompt to install VPN +function _prompt_install_vpn() { + _install_log "Configure VPN support" + echo -n "Install VPN and enable client configuration? [Y/n]: " if [ "$assume_yes" == 0 ]; then read answer < /dev/tty if [ "$answer" != "${answer#[Nn]}" ]; then echo -e else - _install_openvpn + _install_vpn fi + elif [ "$ovpn_option" == 1 ]; then + _install_vpn + else + echo "(Skipped)" + fi +} + +function _install_vpn() { + echo -n "Install [O]penVPN or [W]ireguard? [O/W]: " + if [ "$assume_yes" == 0 ]; then + read answer < /dev/tty + case $answer in + [oO]* ) + _install_openvpn; + break;; + [wW]* ) + _install_wireguard; + esac elif [ "$ovpn_option" == 1 ]; then _install_openvpn else @@ -253,6 +271,20 @@ function _prompt_install_openvpn() { fi } +# Install Wireguard from the Debian unstable distro +function _install_wireguard() { + _install_log "Configure Wireguard support" + 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" + sudo apt-get update && sudo apt-get install $apt_option wireguard || _install_status 1 "Unable to install wireguard" + _install_status 0 +} + # Install openvpn and enable client configuration option function _install_openvpn() { _install_log "Installing OpenVPN and enabling client configuration" @@ -537,7 +569,7 @@ function _install_raspap() { _default_configuration _configure_networking _prompt_install_adblock - _prompt_install_openvpn + _prompt_install_vpn _patch_system_files _install_complete } From 7e58feeec0eae36cef5f56798d1695fe357db22c Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 20 Apr 2020 11:04:36 +0100 Subject: [PATCH 002/300] Enable wg management UI --- installers/common.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/installers/common.sh b/installers/common.sh index e52d62d71..a58e4ba7b 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -254,7 +254,7 @@ function _prompt_install_vpn() { } function _install_vpn() { - echo -n "Install [O]penVPN or [W]ireguard? [O/W]: " + echo -n "Install [O]penVPN or [W]ireGuard? [O/W]: " if [ "$assume_yes" == 0 ]; then read answer < /dev/tty case $answer in @@ -273,15 +273,18 @@ function _install_vpn() { # Install Wireguard from the Debian unstable distro function _install_wireguard() { - _install_log "Configure Wireguard support" - echo "Installing Wireguard from Debian unstable distro" + _install_log "Configure WireGuard support" + 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 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 } From 2bedbad71a89be18656f0ff89ed3e70f72715775 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 20 Apr 2020 11:05:15 +0100 Subject: [PATCH 003/300] Add wireguard constants --- config/config.php | 3 +++ includes/defaults.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/config/config.php b/config/config.php index f638b2a74..50e6442a5 100755 --- a/config/config.php +++ b/config/config.php @@ -21,6 +21,8 @@ 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_SERVER_CONFIG', '/etc/wireguard/wg0.conf'); +define('RASPI_WIREGUARD_CLIENT_CONFIG', '/etc/wireguard/wg0-client.conf'); define('RASPI_TORPROXY_CONFIG', '/etc/tor/torrc'); define('RASPI_LIGHTTPD_CONFIG', '/etc/lighttpd/lighttpd.conf'); @@ -34,6 +36,7 @@ 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/includes/defaults.php b/includes/defaults.php index 9598ce9be..ddf9ea92f 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -25,6 +25,8 @@ '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_SERVER_CONFIG' => '/etc/wireguard/wg0.conf', + 'RASPI_WIREGUARD_CLIENT_CONFIG' => '/etc/wireguard/wg0-client.conf', 'RASPI_TORPROXY_CONFIG' => '/etc/tor/torrc', 'RASPI_LIGHTTPD_CONFIG' => '/etc/lighttpd/lighttpd.conf', @@ -35,6 +37,7 @@ '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, From 4c0de339560f65bed3d9e9e613a5e0e1252567d7 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 20 Apr 2020 12:52:03 +0100 Subject: [PATCH 004/300] Update w/ wg_conf --- index.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/index.php b/index.php index c2fe13ee8..837ebef33 100755 --- a/index.php +++ b/index.php @@ -40,6 +40,7 @@ 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'; $output = $return = 0; @@ -164,6 +165,11 @@ + + + @@ -257,6 +263,9 @@ case "openvpn_conf": DisplayOpenVPNConfig(); break; + case "wg_conf": + DisplayWireGuardConfig(); + break; case "torproxy_conf": DisplayTorProxyConfig(); break; From db497de7d0965cbd11ea3f02031e74e8a4b5af79 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 20 Apr 2020 12:53:46 +0100 Subject: [PATCH 005/300] Initial commit: wg templates --- includes/wireguard.php | 48 +++++++++++++++++++++++++++++++++++++ templates/wg/general.php | 0 templates/wg/logging.php | 0 templates/wireguard.php | 51 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+) create mode 100644 includes/wireguard.php create mode 100644 templates/wg/general.php create mode 100644 templates/wg/logging.php create mode 100644 templates/wireguard.php diff --git a/includes/wireguard.php b/includes/wireguard.php new file mode 100644 index 000000000..a0c2ed948 --- /dev/null +++ b/includes/wireguard.php @@ -0,0 +1,48 @@ +addMessage('Attempting to start WireGuard', 'info'); + exec('sudo /bin/systemctl start wg-quick@wg0', $return); + exec('sudo /bin/systemctl enable 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); + exec('sudo /bin/systemctl disable wg-quick@wg0', $return); + foreach ($return as $line) { + $status->addMessage($line, 'info'); + } + } + } + + exec('pidof wg | wc -l', $wgstatus); + + $serviceStatus = $wgstatus[0] == 0 ? "down" : "up"; + + echo renderTemplate( + "wireguard", compact( + "status", + "serviceStatus" + ) + ); +} + diff --git a/templates/wg/general.php b/templates/wg/general.php new file mode 100644 index 000000000..e69de29bb diff --git a/templates/wg/logging.php b/templates/wg/logging.php new file mode 100644 index 000000000..e69de29bb diff --git a/templates/wireguard.php b/templates/wireguard.php new file mode 100644 index 000000000..b3f33796f --- /dev/null +++ b/templates/wireguard.php @@ -0,0 +1,51 @@ + + + "> + + "> + + "> + + + + +
+
+
+
+
+
+ +
+
+ +
+
+
+
+ showMessages(); ?> +
+ + + + + +
+ + +
+ + +
+
+ +
+
+
+ From 070b1db4257785c6052f23d9a9a3d61ac63ba966 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 20 Apr 2020 22:35:16 +0100 Subject: [PATCH 006/300] Create RaspAP webfont --- dist/raspap/css/fonts/RaspAP.eot | Bin 0 -> 2392 bytes dist/raspap/css/fonts/RaspAP.svg | 12 +++++++ dist/raspap/css/fonts/RaspAP.ttf | Bin 0 -> 2232 bytes dist/raspap/css/fonts/RaspAP.woff | Bin 0 -> 2308 bytes dist/raspap/css/style.css | 54 ++++++++++++++++++++++++++++++ 5 files changed, 66 insertions(+) create mode 100755 dist/raspap/css/fonts/RaspAP.eot create mode 100755 dist/raspap/css/fonts/RaspAP.svg create mode 100755 dist/raspap/css/fonts/RaspAP.ttf create mode 100755 dist/raspap/css/fonts/RaspAP.woff create mode 100644 dist/raspap/css/style.css diff --git a/dist/raspap/css/fonts/RaspAP.eot b/dist/raspap/css/fonts/RaspAP.eot new file mode 100755 index 0000000000000000000000000000000000000000..d77690f6fda5ba960971730d722cece920d258ab GIT binary patch literal 2392 zcmaJ@O>7&-6@G7LcXqiXml92KNsAIGG9e?`q_m_+N^U|YaI9ESL>qD}2dx~Nkr~;R zL<+Ku+CdQn=q0x#w-hN5pg_@{f&gg&15FDTMGgh>gA^@L@ElQyR;f)lX_uZMwbX&! zp-s9)+u--WBxnuH9(1l#kGhm4kIFQQnTOYcQqF?(1__!ydw!-8i5kBI^D)|$jh*(c zyY`Pap_GRF;%58iF2)haYP2@D@4Wii-#>l}@@*o6u65d1lfV7LzlbDcs$T{eRAYjeDYxsMGlPS9pT!2!6I$=3*hAGaWXiH*3m-o2XVAO>We^$LozcM0bMK zEU3A9ZLUy^mpPx4X4PTLxR^oBJFo&%je0})x{CME_yoI^gsUK0v5?D8Gn5Zj!+>9v zjf1L~1Y8$pScTE3SA0gAvY0EtOTmv$b$+eQ^8zC*ugo(H`#D(zU&yH*&xUB#BR09} zo!}}$$;0-5gV~c@#3LR=0Z^??a2z4x0U;C}j>ix^V5MI51{Mp_6n(?Ew6*=|A3ImC z*tVvvZr%9m%NsW?YJC4vVS2ii%|=a4k1TplLg#=LF1Xf+#hHu{vn4ZV#Pea+Ewk(< zIhpi+8c9dY%bqi$jbt4DwKMMMme5Vz2$`BGB5B7-g*DA!VH&ZiSS%NGgtTkNMyHNM z^U+M!Fe;P9McY1B4u%qe+@vA(%;VB}@~N3hKdrQ$U;gO%h|bBpqP2AQY^&RCo$da) z;dy5g@p$5l=QZYm;hnUj!3D!#-m3(N?e5LiDB0pEXrY;3*Rd~We% zb6t5`Z=O`%fVFRv$qOe=oHAZqTH4BFxWyk9i*K&4UpHRc+IqiK1~JIB7~XRiGM6Jy zeoH~!Pn}==?e1{}2IDZxA>kv(4hq)zIh%A-5u*P!-X3BD|4DR+CHT`rY|~MC zU7ZPf089uo1FYd7e|d-n_=O?Xf%k^kz|lTF#1j1S5Zg3I@2=u>e&*bRmxHy=-p%X1 zZq_T$KKe%R<0X$^I-9q)+k1yE89!EA>~;6Eo1JcFuf5;7ntkO?c4?!x((850wCG<# r_9=@?N(a{wbv0?zKE|uyUZFeamuLf8D`@J%QpP*>&w2lCKeGED{AoBn literal 0 HcmV?d00001 diff --git a/dist/raspap/css/fonts/RaspAP.svg b/dist/raspap/css/fonts/RaspAP.svg new file mode 100755 index 000000000..27920e400 --- /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 0000000000000000000000000000000000000000..112214426e9ea9bad281acc07f2eb7fbaf327207 GIT binary patch literal 2232 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 Tu}xU5KxBF)ykmLW{z~}|xDhO% literal 0 HcmV?d00001 diff --git a/dist/raspap/css/style.css b/dist/raspap/css/style.css new file mode 100644 index 000000000..93eb072e5 --- /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.3rem; + 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; +} + From 5179847c5c208939ae4f0f8568e43889e0dd18a3 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 20 Apr 2020 22:36:03 +0100 Subject: [PATCH 007/300] Update w/ project webfont --- index.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/index.php b/index.php index 837ebef33..688c04c57 100755 --- a/index.php +++ b/index.php @@ -83,9 +83,12 @@ - + + + + @@ -119,7 +122,7 @@
Status
@@ -169,7 +172,7 @@ From 5c4814585a79ce64acc36019e0381729df31a175 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 20 Apr 2020 22:36:34 +0100 Subject: [PATCH 008/300] Style tweaks --- app/css/custom.css | 4 ++++ app/css/hackernews.css | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/app/css/custom.css b/app/css/custom.css index 75cff67aa..e5effc31c 100644 --- a/app/css/custom.css +++ b/app/css/custom.css @@ -46,6 +46,10 @@ body { font-weight: 500; } +.sidebar-light hr.sidebar-divider { + padding-top: 0.5rem; +} + .card .card-header { border-color: #d8224c; background-color: #d8224c; diff --git a/app/css/hackernews.css b/app/css/hackernews.css index 7af897a22..b2c364966 100644 --- a/app/css/hackernews.css +++ b/app/css/hackernews.css @@ -66,6 +66,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; @@ -145,6 +148,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 { @@ -181,6 +185,10 @@ ul.nav-tabs, .nav-tabs .nav-link { } } +.fas.fa-circle { + font-size: 0.5rem; +} + .logoutput { width:100%; height:300px; From 8d73fb774f46ff3d85253cc7c789638236dabf5a Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 22 Apr 2020 10:00:34 +0100 Subject: [PATCH 009/300] Update installer + sudoers for wg --- installers/common.sh | 39 +++++++++++++++++++++------------------ installers/raspap.sudoers | 4 ++++ installers/raspbian.sh | 8 ++++++++ 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/installers/common.sh b/installers/common.sh index a58e4ba7b..a06dedda9 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -235,37 +235,37 @@ function _install_adblock() { _install_status 0 } -# Prompt to install VPN -function _prompt_install_vpn() { - _install_log "Configure VPN support" - echo -n "Install VPN and enable client configuration? [Y/n]: " +# Prompt to install openvpn +function _prompt_install_openvpn() { + _install_log "Configure OpenVPN support" + echo -n "Install OpenVPN and enable client configuration? [Y/n]: " if [ "$assume_yes" == 0 ]; then read answer < /dev/tty if [ "$answer" != "${answer#[Nn]}" ]; then echo -e else - _install_vpn + _install_openvpn fi elif [ "$ovpn_option" == 1 ]; then - _install_vpn + _install_openvpn else echo "(Skipped)" fi } -function _install_vpn() { - echo -n "Install [O]penVPN or [W]ireGuard? [O/W]: " +# Prompt to install WireGuard +function _prompt_install_wireguard() { + _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 - case $answer in - [oO]* ) - _install_openvpn; - break;; - [wW]* ) - _install_wireguard; - esac - elif [ "$ovpn_option" == 1 ]; then - _install_openvpn + if [ "$answer" != "${answer#[Nn]}" ]; then + echo -e + else + _install_wireguard + fi + elif [ "$wg_option" == 1 ]; then + _install_wireguard else echo "(Skipped)" fi @@ -283,6 +283,8 @@ function _install_wireguard() { 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 @@ -572,7 +574,8 @@ function _install_raspap() { _default_configuration _configure_networking _prompt_install_adblock - _prompt_install_vpn + _prompt_install_openvpn + _prompt_install_wireguard _patch_system_files _install_complete } diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index 517fbdefd..ef21ec148 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -37,4 +37,8 @@ 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/cp /tmp/dnsmasqdata /etc/dnsmasq.d/090_adblock.conf www-data ALL=(ALL) NOPASSWD:/etc/raspap/adblock/update_blocklist.sh +www-data ALL=(ALL) NOPASSWD:/usr/bin/wg-quick up wg0 +www-data ALL=(ALL) NOPASSWD:/usr/bin/wg-quick down wg0 +www-data ALL=(ALL) NOPASSWD:/usr/bin/wg + diff --git a/installers/raspbian.sh b/installers/raspbian.sh index d0e727690..da8ca5d0a 100755 --- a/installers/raspbian.sh +++ b/installers/raspbian.sh @@ -14,6 +14,8 @@ # Used with -y, --yes, sets OpenVPN install option (0=no install) # -a, --adblock # Used with -y, --yes, sets Adblock install option (0=no install) +# -w, --wireguard +# Used with -y, --yes, sets WireGuard install option (0=no install) # -r, --repo, --repository # Overrides the default GitHub repo (billz/raspap-webgui) # -b, --branch @@ -39,6 +41,7 @@ branch="master" assume_yes=0 ovpn_option=1 adblock_option=1 +wg_option=1 # Define colors readonly ANSI_RED="\033[0;31m" @@ -58,6 +61,7 @@ Usage: raspbian.sh [OPTION]\n -c, --cert, --certificate\n\tInstalls an SSL certificate for lighttpd -o, --openvpn \n\tUsed with -y, --yes, sets OpenVPN install option (0=no install) -a, --adblock \n\tUsed with -y, --yes, sets Adblock install option (0=no install) +-w, --wireguard \n\tUsed with -y, --yes, sets WireGuard install option (0=no install) -r, --repo, --repository \n\tOverrides the default GitHub repo (billz/raspap-webgui) -b, --branch \n\tOverrides the default git branch (master) -h, --help\n\tOutputs usage notes and exits @@ -80,6 +84,10 @@ while :; do adblock_option="$2" shift ;; + -w|--wireguard) + wg_option="$2" + shift + ;; -c|--cert|--certificate) install_cert=1 ;; From 7c7b8941cbabc64ee688a643a3cd34c4fb3af745 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 22 Apr 2020 10:01:31 +0100 Subject: [PATCH 010/300] Update stop/start, status --- includes/wireguard.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/includes/wireguard.php b/includes/wireguard.php index a0c2ed948..ed94ee34c 100644 --- a/includes/wireguard.php +++ b/includes/wireguard.php @@ -19,28 +19,28 @@ function DisplayWireGuardConfig() } } elseif (isset($_POST['startwg'])) { $status->addMessage('Attempting to start WireGuard', 'info'); - exec('sudo /bin/systemctl start wg-quick@wg0', $return); - exec('sudo /bin/systemctl enable wg-quick@wg0', $return); + exec('sudo /usr/bin/wg-quick up 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); - exec('sudo /bin/systemctl disable wg-quick@wg0', $return); + exec('sudo /usr/bin/wg-quick down wg0', $return); foreach ($return as $line) { $status->addMessage($line, 'info'); } } } - exec('pidof wg | wc -l', $wgstatus); + 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" ) ); From 292a4ed1beb109286c81b989bbb7bdfc8ab3775d Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 22 Apr 2020 10:02:07 +0100 Subject: [PATCH 011/300] Initial template setup --- templates/wg/general.php | 46 ++++++++++++++++++++++++++++++++++++++++ templates/wg/logging.php | 11 ++++++++++ templates/wg/peers.php | 11 ++++++++++ templates/wireguard.php | 12 ++++++----- 4 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 templates/wg/peers.php diff --git a/templates/wg/general.php b/templates/wg/general.php index e69de29bb..ec44562a6 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -0,0 +1,46 @@ + +
+
+
+

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

+ +

+
+
+
+ + +
+
+ +
+
+ +
+
+ +
+ + +
+
+
+ +
+
+ + +
+
+ +
+
+
+ diff --git a/templates/wg/logging.php b/templates/wg/logging.php index e69de29bb..eb31dd10f 100644 --- a/templates/wg/logging.php +++ b/templates/wg/logging.php @@ -0,0 +1,11 @@ + +
+
+
+

+ + +
+
+
+ diff --git a/templates/wg/peers.php b/templates/wg/peers.php new file mode 100644 index 000000000..2edc4d2d6 --- /dev/null +++ b/templates/wg/peers.php @@ -0,0 +1,11 @@ + +
+
+
+

+ + +
+
+
+ diff --git a/templates/wireguard.php b/templates/wireguard.php index b3f33796f..0c0c0a817 100644 --- a/templates/wireguard.php +++ b/templates/wireguard.php @@ -1,8 +1,8 @@ "> - - "> + + "> "> @@ -15,7 +15,7 @@
- +

- +

- +
diff --git a/templates/wg/peers.php b/templates/wg/peers.php index 2edc4d2d6..16337a081 100644 --- a/templates/wg/peers.php +++ b/templates/wg/peers.php @@ -1,8 +1,51 @@
+
-

+

+
+ + +
+ aria-describedby="endpoint-description"> + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ +
+
+ +
+ + +
+
+
diff --git a/templates/wireguard.php b/templates/wireguard.php index 0c0c0a817..3ead09c7a 100644 --- a/templates/wireguard.php +++ b/templates/wireguard.php @@ -32,7 +32,7 @@ From 543791f7238cb40ec05ff5a4c4e6b0aad494cdb6 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 25 Aug 2020 22:11:27 +0100 Subject: [PATCH 015/300] WIP: handle input --- includes/wireguard.php | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/includes/wireguard.php b/includes/wireguard.php index ed94ee34c..34d56ce21 100644 --- a/includes/wireguard.php +++ b/includes/wireguard.php @@ -11,12 +11,31 @@ function DisplayWireGuardConfig() $status = new StatusMessages(); if (!RASPI_MONITOR_ENABLED) { if (isset($_POST['savewgettings'])) { + # Todo: validate input if (isset($_POST['authUser'])) { - $authUser = strip_tags(trim($_POST['authUser'])); + $peer_id = strip_tags(trim($_POST'peer_id'])); } - if (isset($_POST['authPassword'])) { - $authPassword = strip_tags(trim($_POST['authPassword'])); + if (isset($_POST['wg_endpoint'])) { + $wg_endpoint = strip_tags(trim($_POST['wg_endpoint'])); } + if (isset($_POST['wg_allowedips'])) { + $wg_allowedips = strip_tags(trim($_POST['wg_allowedips'])); + } + if (isset($_POST['wg_pkeepalive'])) { + $wg_pkeepalive = strip_tags(trim($_POST['wg_pkeepalive'])); + } + if (isset($_POST['wg_peerpubkey'])) { + $wg_endpoint = strip_tags(trim($_POST['wg_peerpubkey'])); + } + file_put_contents("/tmp/wgdata", $config); + system('sudo cp /tmp/wgdata '.RASPI_WIREGUARD_CONFIG, $return); + + if ($return == 0) { + $status->addMessage('Wireguard configuration updated successfully', 'success'); + } else { + $status->addMessage('Wireguard configuration failed to be updated.', 'danger'); + } + } elseif (isset($_POST['startwg'])) { $status->addMessage('Attempting to start WireGuard', 'info'); exec('sudo /usr/bin/wg-quick up wg0', $return); @@ -41,7 +60,13 @@ function DisplayWireGuardConfig() "wireguard", compact( "status", "wg_state", - "serviceStatus" + "serviceStatus", + "endpoint_enable", + "peer_id", + "wg_endpoint", + "wg_allowedips", + "wg_pkeepalive", + "wg_peerpubkey" ) ); } From 22651a86b7d3b5b327f9ecd5398076a4cbee2976 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 25 Aug 2020 22:11:57 +0100 Subject: [PATCH 016/300] Simplify wg config handling --- config/config.php | 3 +-- includes/defaults.php | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/config/config.php b/config/config.php index 6de2822fd..122b1f942 100755 --- a/config/config.php +++ b/config/config.php @@ -21,8 +21,7 @@ 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_SERVER_CONFIG', '/etc/wireguard/wg0.conf'); -define('RASPI_WIREGUARD_CLIENT_CONFIG', '/etc/wireguard/wg0-client.conf'); +define('RASPI_WIREGUARD_CONFIG', '/etc/wireguard/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'); diff --git a/includes/defaults.php b/includes/defaults.php index 6f6251e95..253c56196 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -26,8 +26,7 @@ '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_SERVER_CONFIG' => '/etc/wireguard/wg0.conf', - 'RASPI_WIREGUARD_CLIENT_CONFIG' => '/etc/wireguard/wg0-client.conf', + 'RASPI_WIREGUARD_CONFIG' => '/etc/wireguard/wg0.conf', 'RASPI_TORPROXY_CONFIG' => '/etc/tor/torrc', 'RASPI_LIGHTTPD_CONFIG' => '/etc/lighttpd/lighttpd.conf', 'RASPI_ACCESS_CHECK_IP' => '1.1.1.1', From aff035122b78220ef226e2b2320cc66bdb05798c Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 26 Aug 2020 23:54:49 +0100 Subject: [PATCH 017/300] Validate, save & display wg config --- includes/wireguard.php | 58 +++++++++++++++++++++++++++++++-------- installers/raspap.sudoers | 2 ++ templates/wg/general.php | 2 +- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/includes/wireguard.php b/includes/wireguard.php index 34d56ce21..d2ec10881 100644 --- a/includes/wireguard.php +++ b/includes/wireguard.php @@ -11,25 +11,47 @@ function DisplayWireGuardConfig() $status = new StatusMessages(); if (!RASPI_MONITOR_ENABLED) { if (isset($_POST['savewgettings'])) { - # Todo: validate input - if (isset($_POST['authUser'])) { - $peer_id = strip_tags(trim($_POST'peer_id'])); + // Validate input + $good_input = true; + $peer_id = 1; + if (isset($_POST['peer_id'])) { + $peer_id = escapeshellarg($_POST['peer_id']); } if (isset($_POST['wg_endpoint'])) { - $wg_endpoint = strip_tags(trim($_POST['wg_endpoint'])); + if (!filter_var($_POST['wg_endpoint'], FILTER_VALIDATE_IP)) { + $status->addMessage('Invalid value for endpoint address', 'danger'); + $good_input = false; + } else { + $wg_endpoint = escapeshellarg($_POST['wg_endpoint']); + } } if (isset($_POST['wg_allowedips'])) { - $wg_allowedips = strip_tags(trim($_POST['wg_allowedips'])); + if (!filter_var($_POST['wg_allowedips'], FILTER_VALIDATE_IP)) { + $status->addMessage('Invalid value for allowed IPs', 'danger'); + $good_input = false; + } else { + $wg_allowedips = escapeshellarg($_POST['wg_allowedips']); + } } if (isset($_POST['wg_pkeepalive'])) { - $wg_pkeepalive = strip_tags(trim($_POST['wg_pkeepalive'])); + if (strlen($_POST['wg_pkeepalive']) > 4 || !is_numeric($_POST['wg_pkeepalive'])) { + $status->addMessage('Invalid value for persistent keepalive', 'danger'); + $good_input = false; + } else { + $wg_pkeepalive = escapeshellarg($_POST['wg_pkeepalive']); + } } if (isset($_POST['wg_peerpubkey'])) { $wg_endpoint = strip_tags(trim($_POST['wg_peerpubkey'])); } - file_put_contents("/tmp/wgdata", $config); - system('sudo cp /tmp/wgdata '.RASPI_WIREGUARD_CONFIG, $return); - + // Save settings + if ($good_input) { + file_put_contents("/tmp/wgdata", $config); + system('sudo cp /tmp/wgdata '.RASPI_WIREGUARD_CONFIG, $return); + foreach ($return as $line) { + $status->addMessage($line, 'info'); + } + } if ($return == 0) { $status->addMessage('Wireguard configuration updated successfully', 'success'); } else { @@ -51,8 +73,18 @@ function DisplayWireGuardConfig() } } - exec('pidof wg-crypt-wg0 | wc -l', $wgstatus); + // fetch wg config + exec('sudo cat '. RASPI_WIREGUARD_CONFIG, $return); + $conf = ParseConfig($return); + $wg_port = $conf['ListenPort']; + $wg_ipaddress = $conf['Address']; + $wg_pubkey = $conf['PublicKey']; + $wg_endpoint = $conf['Endpoint']; + $wg_allowedips = $conf['AllowedIPs']; + $wg_pkeepalive = $conf['PersistentKeepalive']; + // fetch service status + exec('pidof wg-crypt-wg0 | wc -l', $wgstatus); $serviceStatus = $wgstatus[0] == 0 ? "down" : "up"; $wg_state = ($wgstatus[0] > 0); @@ -63,10 +95,12 @@ function DisplayWireGuardConfig() "serviceStatus", "endpoint_enable", "peer_id", + "wg_port", + "wg_ipaddress", + "wg_pubkey", "wg_endpoint", "wg_allowedips", - "wg_pkeepalive", - "wg_peerpubkey" + "wg_pkeepalive" ) ); } diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index 2d6ed4950..517b62333 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -41,5 +41,7 @@ www-data ALL=(ALL) NOPASSWD:/etc/raspap/adblock/update_blocklist.sh www-data ALL=(ALL) NOPASSWD:/usr/bin/wg-quick up wg0 www-data ALL=(ALL) NOPASSWD:/usr/bin/wg-quick down wg0 www-data ALL=(ALL) NOPASSWD:/usr/bin/wg +www-data ALL=(ALL) NOPASSWD:/bin/cat /etc/wireguard/wg0.conf + diff --git a/templates/wg/general.php b/templates/wg/general.php index 686949bbe..b97635555 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -36,7 +36,7 @@
- +
From 34b5b4c1b2ea52562e87bc8217cbac641994f8a4 Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 28 Aug 2020 23:40:46 +0100 Subject: [PATCH 018/300] Add validateCidr() --- includes/functions.php | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/includes/functions.php b/includes/functions.php index 4d2993148..c58b2b131 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -452,3 +452,30 @@ 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; +} + From af0721e0214361979d63938ac1eecde5c50603ba Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 28 Aug 2020 23:42:55 +0100 Subject: [PATCH 019/300] Save wg config, template fixes --- includes/wireguard.php | 67 +++++++++++++++++++++++++++------------- templates/wg/general.php | 7 +++-- 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/includes/wireguard.php b/includes/wireguard.php index d2ec10881..26badcd0a 100644 --- a/includes/wireguard.php +++ b/includes/wireguard.php @@ -10,52 +10,73 @@ function DisplayWireGuardConfig() { $status = new StatusMessages(); if (!RASPI_MONITOR_ENABLED) { - if (isset($_POST['savewgettings'])) { - // Validate input + if (isset($_POST['savewgsettings'])) { + // Set defaults $good_input = true; $peer_id = 1; - if (isset($_POST['peer_id'])) { - $peer_id = escapeshellarg($_POST['peer_id']); + // Validate input + if (isset($_POST['wg_port'])) { + if (strlen($_POST['wg_port']) > 5 || !is_numeric($_POST['wg_port'])) { + $status->addMessage('Invalid value for port number', 'danger'); + $good_input = false; + } } - if (isset($_POST['wg_endpoint'])) { - if (!filter_var($_POST['wg_endpoint'], FILTER_VALIDATE_IP)) { + if (isset($_POST['wg_ipaddress'])) { + if (!validateCidr($_POST['wg_ipaddress'])) { + $status->addMessage('Invalid value for IP address', 'danger'); + $good_input = false; + } + } + if (isset($_POST['wg_endpoint']) && strlen(trim($_POST['wg_endpoint']) >0 )) { + if (!validateCidr($_POST['wg_endpoint'])) { $status->addMessage('Invalid value for endpoint address', 'danger'); $good_input = false; - } else { - $wg_endpoint = escapeshellarg($_POST['wg_endpoint']); } } if (isset($_POST['wg_allowedips'])) { - if (!filter_var($_POST['wg_allowedips'], FILTER_VALIDATE_IP)) { + if (!validateCidr($_POST['wg_allowedips'])) { $status->addMessage('Invalid value for allowed IPs', 'danger'); $good_input = false; - } else { - $wg_allowedips = escapeshellarg($_POST['wg_allowedips']); } } - if (isset($_POST['wg_pkeepalive'])) { + 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; - } else { - $wg_pkeepalive = escapeshellarg($_POST['wg_pkeepalive']); } } - if (isset($_POST['wg_peerpubkey'])) { - $wg_endpoint = strip_tags(trim($_POST['wg_peerpubkey'])); - } // Save settings if ($good_input) { + $config[] = '[Interface]'; + $config[] = 'Address = '.$_POST['wg_ipaddress']; + $config[] = 'ListenPort = '.$_POST['wg_port']; + $config[] = ''; + $config[] = 'PrivateKey = '.$_POST['wg_privkey']; + $config[] = 'PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE'; + $config[] = 'PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE'; + $config[] = ''; + $config[] = '[Peer]'; + $config[] = 'PublicKey = '.$_POST['wg_pubkey']; + if ($_POST['wg_endpoint'] !== '') { + $config[] = 'Endpoint = '.trim($_POST['wg_endpoint']); + } + $config[] = 'AllowedIPs = '.$_POST['wg_allowedips']; + 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); 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'); + if ($return == 0) { + $status->addMessage('Wireguard configuration updated successfully', 'success'); + } else { + $status->addMessage('Wireguard configuration failed to be updated.', 'danger'); + } } } elseif (isset($_POST['startwg'])) { @@ -79,6 +100,7 @@ function DisplayWireGuardConfig() $wg_port = $conf['ListenPort']; $wg_ipaddress = $conf['Address']; $wg_pubkey = $conf['PublicKey']; + $wg_privkey = $conf['PrivateKey']; $wg_endpoint = $conf['Endpoint']; $wg_allowedips = $conf['AllowedIPs']; $wg_pkeepalive = $conf['PersistentKeepalive']; @@ -98,6 +120,7 @@ function DisplayWireGuardConfig() "wg_port", "wg_ipaddress", "wg_pubkey", + "wg_privkey", "wg_endpoint", "wg_allowedips", "wg_pkeepalive" diff --git a/templates/wg/general.php b/templates/wg/general.php index b97635555..aa1de7962 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -16,7 +16,7 @@
- +
@@ -25,13 +25,14 @@
- +
- +
+
From 7286173438e2bfc736cf359008964eeb66a4981a Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 5 Sep 2020 19:27:38 +0100 Subject: [PATCH 020/300] Add rudimentary logging --- includes/wireguard.php | 6 ++++++ installers/raspap.sudoers | 3 +++ templates/wg/logging.php | 14 +++++++++++--- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/includes/wireguard.php b/includes/wireguard.php index 26badcd0a..84956b892 100644 --- a/includes/wireguard.php +++ b/includes/wireguard.php @@ -69,6 +69,11 @@ function DisplayWireGuardConfig() file_put_contents("/tmp/wgdata", $config); system('sudo cp /tmp/wgdata '.RASPI_WIREGUARD_CONFIG, $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'); } @@ -115,6 +120,7 @@ function DisplayWireGuardConfig() "status", "wg_state", "serviceStatus", + "wg_log", "endpoint_enable", "peer_id", "wg_port", diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index 517b62333..4c813d0e7 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -35,9 +35,12 @@ www-data ALL=(ALL) NOPASSWD:/etc/raspap/lighttpd/configport.sh www-data ALL=(ALL) NOPASSWD:/etc/raspap/openvpn/configauth.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/wgdata /etc/wireguard/wg0.conf www-data ALL=(ALL) NOPASSWD:/etc/raspap/adblock/update_blocklist.sh +www-data ALL=(ALL) NOPASSWD:/usr/bin/tee /tmp/wireguard.log +www-data ALL=(ALL) NOPASSWD:/bin/systemctl status wg-quick@wg0 www-data ALL=(ALL) NOPASSWD:/usr/bin/wg-quick up wg0 www-data ALL=(ALL) NOPASSWD:/usr/bin/wg-quick down wg0 www-data ALL=(ALL) NOPASSWD:/usr/bin/wg diff --git a/templates/wg/logging.php b/templates/wg/logging.php index eb31dd10f..c9cb41850 100644 --- a/templates/wg/logging.php +++ b/templates/wg/logging.php @@ -1,10 +1,18 @@
-
+

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

+ '.htmlspecialchars($log, ENT_QUOTES).''; + ?>
From 31edb21a764bfdc185469afb07dd295cb529d2f5 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 23 Sep 2020 09:10:44 +0100 Subject: [PATCH 021/300] Fix merge error --- includes/functions.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/includes/functions.php b/includes/functions.php index 85c69ca6c..4a9e77bb6 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -477,8 +477,9 @@ function validateCidr($cidr) return $netmask <= 128; } return false; - - // Validates a host or FQDN +} + +// 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); } From 06c8a2edcdc99194d18d470218a8740407b19567 Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 15 Oct 2020 16:08:23 +0100 Subject: [PATCH 022/300] Install raspberrypi-kernel-headers (raspbian only) --- installers/common.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/installers/common.sh b/installers/common.sh index 9c55a9f00..2eb36e89f 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -283,6 +283,10 @@ function _prompt_install_wireguard() { # 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" From 0ca49ad6452661e7ac08f943e691df240aa81adc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Feb 2021 10:12:17 +0000 Subject: [PATCH 023/300] Bump minimist from 1.2.0 to 1.2.5 Bumps [minimist](https://github.com/substack/minimist) from 1.2.0 to 1.2.5. - [Release notes](https://github.com/substack/minimist/releases) - [Commits](https://github.com/substack/minimist/compare/1.2.0...1.2.5) Signed-off-by: dependabot[bot] --- yarn.lock | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2b445d7fb..b225fa08f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2744,16 +2744,11 @@ minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: dependencies: brace-expansion "^1.1.7" -minimist@^1.1.3, minimist@^1.2.5: +minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= - minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" From fb6fdafc9ebc0b5ed7ce952cce10c9ce0f8405f1 Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Thu, 4 Feb 2021 18:22:15 +0100 Subject: [PATCH 024/300] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cf7a707b0..85135021d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![](https://i.imgur.com/xeKD93p.png) +![](https://i.imgur.com/DpgvLIO.png) [![Release 2.6](https://img.shields.io/badge/release-v2.6-green)](https://github.com/raspap/raspap-webgui/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/) RaspAP lets you quickly get a wireless access point up and running to share the connectivity of many popular [Debian-based devices](#supported-operating-systems), including the Raspberry Pi. Our popular [Quick installer](#quick-installer) creates a known-good default configuration that "just works" on all current Raspberry Pis with onboard wireless. A responsive interface gives you control over the relevant services and networking options. Advanced DHCP settings, OpenVPN client support, SSL, security audits, themes and multilingual options are included. From e2be5dc17dcfc11513c9d738e155e499cd1d0b1a Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Fri, 5 Feb 2021 11:17:30 +0100 Subject: [PATCH 025/300] Update README.md --- README.md | 206 +++++++++--------------------------------------------- 1 file changed, 33 insertions(+), 173 deletions(-) diff --git a/README.md b/README.md index 85135021d..5c6282cd0 100644 --- a/README.md +++ b/README.md @@ -1,199 +1,59 @@ ![](https://i.imgur.com/DpgvLIO.png) [![Release 2.6](https://img.shields.io/badge/release-v2.6-green)](https://github.com/raspap/raspap-webgui/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/) -RaspAP lets you quickly get a wireless access point up and running to share the connectivity of many popular [Debian-based devices](#supported-operating-systems), including the Raspberry Pi. Our popular [Quick installer](#quick-installer) creates a known-good default configuration that "just works" on all current Raspberry Pis with onboard wireless. A responsive interface gives you control over the relevant services and networking options. Advanced DHCP settings, OpenVPN client support, SSL, security audits, themes and multilingual options are included. - -RaspAP has been featured on sites such as [Instructables](http://www.instructables.com/id/Raspberry-Pi-As-Completely-Wireless-Router/), [Adafruit](https://blog.adafruit.com/2016/06/24/raspap-wifi-configuration-portal-piday-raspberrypi-raspberry_pi/), [Raspberry Pi Weekly](https://www.raspberrypi.org/weekly/commander/) and [Awesome Raspberry Pi](https://project-awesome.org/thibmaek/awesome-raspberry-pi) and implemented in countless projects. - -We hope you enjoy using RaspAP as much as we do creating it. Tell us how you use this with [your own projects](https://github.com/raspap/raspap-awesome). +Welcome to RaspAP Insiders. You, the members of the Insiders community, support the sponsorware release strategy, 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*. ![](https://i.imgur.com/ikWvsMG.gif) -![](https://i.imgur.com/EiIpdOS.gif) -![](https://i.imgur.com/eCjUS1H.gif) -![](https://i.imgur.com/5FT2BcS.gif) -![](https://i.imgur.com/RKaBFrZ.gif) -## Contents - - - [Prerequisites](#prerequisites) - - [Quick installer](#quick-installer) - - [Ad Blocking](#ad-blocking) - - [Bridged AP](#bridged-ap) - - [Simultaneous AP and Wifi client](#simultaneous-ap-and-wifi-client) - - [Support us](#support-us) - - [Manual installation](#manual-installation) - - [802.11ac 5GHz support](#80211ac-5ghz-support) - - [Supported operating systems](#supported-operating-systems) - - [Multilingual support](#multilingual-support) - - [HTTPS support](#https-support) - - [OpenVPN support](#openvpn-support) - - [How to contribute](#how-to-contribute) - - [Reporting issues](#reporting-issues) - - [License](#license) - -## Prerequisites -Start with a clean install of the [latest release of Raspberry Pi OS (32-bit) Lite](https://www.raspberrypi.org/software/operating-systems/#raspberry-pi-os-32-bit). The Raspberry Pi OS desktop and 64-bit beta distros are unsupported. - -1. Update Raspbian, including the kernel and firmware, followed by a reboot: -``` -sudo apt-get update -sudo apt-get full-upgrade -sudo reboot -``` -2. Set the "WLAN country" option in `raspi-config`'s **Localisation Options**: `sudo raspi-config` - -3. If you have a device without an onboard wireless chipset, the [**Edimax Wireless 802.11b/g/n nano USB adapter**](https://www.edimax.com/edimax/merchandise/merchandise_detail/data/edimax/global/wireless_adapters_n150/ew-7811un) is an excellent option – it's small, cheap and has good driver support. - -With the prerequisites done, you can proceed with either the Quick installer or Manual installation steps below. - -## Quick installer -Install RaspAP from your device's shell prompt: -```sh -curl -sL https://install.raspap.com | bash -``` -The [installer](https://docs.raspap.com/quick/) will complete the steps in the manual installation (below) for you. - -After the reboot at the end of the installation the wireless network will be -configured as an access point as follows: -* IP address: 10.3.141.1 - * Username: admin - * Password: secret -* DHCP range: 10.3.141.50 to 10.3.141.255 -* SSID: `raspi-webgui` -* Password: ChangeMe - -**Note:** As the name suggests, the Quick Installer is a great way to quickly setup a new AP. However, it does not automagically detect the unique configuration of your system. Best results are obtained by connecting to ethernet (`eth0`) or as a WiFi client, also known as managed mode, with `wlan0`. For the latter, refer to [this FAQ](https://docs.raspap.com/faq/#headless). Special instructions for the Pi Zero W are [available here](https://docs.raspap.com/ap-sta/). - -Please [read this](https://docs.raspap.com/issues/) before reporting an issue. - -## Ad Blocking -This feature uses DNS blacklisting to block requests for ads, trackers and other undesirable hosts. To enable ad blocking, simply respond to the prompt during the installation. As a beta release, we encourage testing and feedback from users of RaspAP. - -Details are [provided here](https://docs.raspap.com/adblock/). - -## Bridged AP -By default RaspAP configures a routed AP for your clients to connect to. A bridged AP configuration is also possible. Slide the **Bridged AP mode** toggle under the **Advanced** tab of **Configure hotspot**, then save and restart the hotspot. - -![](https://i.imgur.com/J5VKSay.png) - -**Note:** In bridged mode, all routing capabilities are handled by your upstream router. Because your router assigns IP addresses to your device's hotspot and its clients, you might not be able to reach the RaspAP web interface from the default `10.3.141.1` address. Instead use your RPi's hostname followed by `.local` to access the RaspAP web interface. With Raspbian default settings, this should look like `raspberrypi.local`. Alternate methods are [discussed here](https://www.raspberrypi.org/documentation/remote-access/ip-address.md). - -More information on Bridged AP mode is provided [in our documentation](https://docs.raspap.com/bridged/). - -## Simultaneous AP and Wifi client -RaspAP lets you create an AP with a Wifi client configuration, often called [AP-STA mode](https://docs.raspap.com/ap-sta/). With your system configured in managed mode, enable the AP from the **Advanced** tab of **Configure hotspot** by sliding the **Wifi client AP mode** toggle. Save settings and start the hotspot. The managed mode AP is functional without restart. - -**Note:** This option is disabled until you configure your system as a wireless client. For a device operating in [managed mode](https://docs.raspap.com/faq/#headless) without an `eth0` connection, this configuration must be enabled [_before_ a reboot](https://docs.raspap.com/ap-sta/). - -## Support us -RaspAP is free software, but powered by your support. If you find RaspAP useful for your personal or commercial projects, [become a GitHub sponsor](https://github.com/sponsors/RaspAP/) or join the project on [Open Collective](https://opencollective.com/RaspAP). Either of these options makes a big difference! -## Manual installation -Detailed manual setup instructions are provided [on our documentation site](https://docs.raspap.com/manual/). - -## 802.11ac 5GHz support -RaspAP provides an 802.11ac wireless mode option for supported hardware (currently the RPi 3B+/4 and compatible Orange Pi models) and wireless regulatory domains. See [this FAQ](https://docs.raspap.com/faq/#80211ac) for more information. - -## Supported operating systems -RaspAP was originally made for Raspbian, but now also installs on the following Debian-based distros. - -| Distribution | Release | Architecture | Support | -|---|:---:|:---:|:---:| -| Raspberry Pi OS | (32-bit) Lite Buster | ARM | Official | -| Armbian | Buster | [ARM](https://docs.armbian.com/#supported-chips) | Official | -| Debian | Buster | ARM / x86_64 | Beta | -| Ubuntu | 18.04 LTS / 19.10 | ARM / x86_64 | Beta | - -![](https://i.imgur.com/luiyYNw.png) - -We find Armbian particularly well-suited for this project. Please note that "supported" is not a guarantee. If you are able to improve support for your preferred distro, we encourage you to [actively contribute](#how-to-contribute) to the project. - -## Multilingual support -RaspAP uses [GNU Gettext](https://www.gnu.org/software/gettext/) to manage multilingual messages. In order to use RaspAP with one of our supported translations, you must configure a corresponding language package on your RPi. To list languages currently installed on your system, use `locale -a` at the shell prompt. To generate new locales, run `sudo dpkg-reconfigure locales` and select any other desired locales. Details are provided on our [documentation site](https://docs.raspap.com/translations/) - -The following translations are currently maintained by the project: - -- Čeština -- 正體中文 (Chinese traditional) -- 简体中文 (Chinese Simplified) -- Dansk -- Deutsch -- Español -- Finnish -- Français -- Ελληνικά (Greek) -- Indonesian -- Italiano -- 日本語 (Japanese) -- 한국어 (Korean) -- Nederlands -- Polskie -- Português -- Русский -- Svenska -- Türkçe -- Tiếng Việt (Vietnamese) - -If your language is not in the list above, why not [contribute a translation](https://docs.raspap.com/translations/#contributing-to-a-translation)? Contributors will receive credit as the original translators. - -## HTTPS support -The Quick Installer may be used to [generate SSL certificates](https://docs.raspap.com/ssl-quick/) with `mkcert`. The installer automates the manual steps [described here](https://docs.raspap.com/ssl-manual/), including configuring lighttpd with SSL support. - -Simply append the `-c` or `--cert` option to the Quick Installer, like so: - -```sh -curl -sL https://install.raspap.com | bash -s -- --cert -``` +## Contents -**Note**: this only installs mkcert and generates an SSL certificate with the input you provide. It does *not* (re)install RaspAP. + - [How sponsorship works](#how-sponsorship-works) + - [About your sponsorship](#about-your-sponsorhip) + - [Exclusive features](#exclusive-features) + - [Funding targets](#funding-targets) + - [Frequently asked questions](#frequently-asked-questions) -More information on SSL certificates and HTTPS support is available [in our documentation](https://docs.raspap.com/ssl-quick/). +## How sponsorship works +New features first land in Insiders, which means that *sponsors will have access to them immediately*. Every feature is tied to a funding goal in monthly subscriptions. When a funding goal is hit, the features that are tied to it are merged back into the [public RaspAP repository](https://github.com/RaspAP/raspap-webgui) and released for general availability. Bugfixes and minor enhancements are always released simultaneously in both editions. -## OpenVPN support -OpenVPN may be optionally installed by the Quick Installer. Once this is done, you can manage client configuration and the `openvpn-client` service with RaspAP. +Don't want to sponsor? No problem, RaspAP already has tons of features available, so chances are that most of your requirements are already satisfied. See the list of [exclusive features](#exclusive-features) to learn which features are currently only available to sponsors. -To configure an OpenVPN client, upload a valid .ovpn file and, optionally, specify your login credentials. RaspAP will store your client configuration and add firewall rules to forward traffic from OpenVPN's `tun0` interface to your configured wireless interface. +## About your sponsorship +Your sponsorship is through your individual or organization's GitHub account. By visiting [**RaspAP's sponsor profile**](), you may change your sponsorship tier or cancel your sponsorship anytime. [1](#footnote-1) -**Note**: this feature is currently in beta. Please [read this](https://docs.raspap.com/faq/#openvpn-fails) before reporting an issue. +As part of the initial rollout of Insiders, all previous one-time backers of RaspAP on GitHub, PayPal or [Open Collective](https://opencollective.com/raspap) will receive unlimited access to Insiders. This is a small gesture for those early financial supporters who recognized the potential of this project. -## How to contribute -1. Fork the project in your account and create a new branch: `your-great-feature`. -2. Open an issue in the repository describing the feature contribution you'd like to make. -3. Commit changes in your feature branch. -4. Open a pull request and reference the initial issue in the pull request message. -Find out more about our [coding style guidelines and recommended tools](CONTRIBUTING.md). +**Important**: If you're sponsoring [RaspAP](https://github.com/RaspAP/sponsors) 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) -## Reporting issues -Please [read this](https://docs.raspap.com/issues/) before reporting a bug. +## 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. -## Contributors + ✅ Manage OpenVPN client configs -### Code Contributors -This project exists thanks to all the awesome people who [contribute](CONTRIBUTING.md) their time and expertise. +We feel that using input from our Insiders to drive the development of this project is a great approach. 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](/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. -### Financial Contributors -Become a [financial contributor](https://opencollective.com/raspap/contribute) and help us sustain our community. +### $500 +✅ Manage OpenVPN client configs -#### Individuals - +## Frequently asked questions -#### Organizations +### Terms +*We're using RaspAP to for a commercial project. Can we use Insiders under the same terms and conditions?* -[Support this project](https://opencollective.com/raspap/contribute) with your organization. Your logo will show up here with a link to your website. +Yes. Whether you're an individual or a company, you may use RaspAP Insiders precisely under the same terms as RaspAP, which are defined by the GNU GPL 3.0 license. However, we kindly ask you to respect the following guidelines: - - - - - - - - - - +* Please **don't distribute the source code** of Insiders. You may freely use it for public, private or commercial projects, fork it, mirror it, do whatever you want with it, but please don't release the source code, as it would counteract the sponsorware strategy. +* If you cancel your subscription, you're removed as a collaborator and will miss out on future updates of Insiders. However, you may *use the latest version* that's available to you as long as you like. Just remember that [GitHub deletes private forks](https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/removing-a-collaborator-from-a-personal-repository). ## License See the [LICENSE](./LICENSE) file. +### Footnotes + +1. If you cancel your sponsorship, GitHub schedules a cancellation request which will become effective at the end of the billing cycle, which ends at the 22nd of a month for monthly sponsorships. This means that even though you cancel your sponsorship, you will keep your access to Insiders as long as your cancellation isn't effective. All charges are processed by GitHub through Stripe. As we don't receive any information regarding your payment, and GitHub doesn't offer refunds, sponsorships are non-refundable. + +2. It's currently not possible to grant access to each member of an organization, as GitHub only allows for adding users. Thus, after sponsoring, please send an email to sponsors@raspap.com, stating which account should become a collaborator of the Insiders repository. We're working on a solution which will make access to organizations much simpler. From c53dede0db0f624ee312e32e9dd0e4174bc9e47b Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Fri, 5 Feb 2021 11:34:04 +0100 Subject: [PATCH 026/300] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 5c6282cd0..87fa93960 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,6 @@ Welcome to RaspAP Insiders. You, the members of the Insiders community, support the sponsorware release strategy, 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*. -![](https://i.imgur.com/ikWvsMG.gif) - ## Contents - [How sponsorship works](#how-sponsorship-works) From 8ed6561c416859eb0090dcdd159f864f818f345e Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Fri, 5 Feb 2021 11:43:00 +0100 Subject: [PATCH 027/300] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 87fa93960..ea78e8b89 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Your sponsorship is through your individual or organization's GitHub account. By As part of the initial rollout of Insiders, all previous one-time backers of RaspAP on GitHub, PayPal or [Open Collective](https://opencollective.com/raspap) will receive unlimited access to Insiders. This is a small gesture for those early financial supporters who recognized the potential of this project. -**Important**: If you're sponsoring [RaspAP](https://github.com/RaspAP/sponsors) 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) +> ℹ️ **Important**: If you're sponsoring [RaspAP](https://github.com/RaspAP/sponsors) 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. @@ -40,7 +40,7 @@ Following is a list of funding targets. When a funding target is reached, the fe ## Frequently asked questions ### Terms -*We're using RaspAP to for a commercial project. Can we use Insiders under the same terms and conditions?* +*We're using RaspAP for a commercial project. Can we use Insiders under the same terms and conditions?* Yes. Whether you're an individual or a company, you may use RaspAP Insiders precisely under the same terms as RaspAP, which are defined by the GNU GPL 3.0 license. However, we kindly ask you to respect the following guidelines: @@ -52,6 +52,6 @@ See the [LICENSE](./LICENSE) file. ### Footnotes -1. If you cancel your sponsorship, GitHub schedules a cancellation request which will become effective at the end of the billing cycle, which ends at the 22nd of a month for monthly sponsorships. This means that even though you cancel your sponsorship, you will keep your access to Insiders as long as your cancellation isn't effective. All charges are processed by GitHub through Stripe. As we don't receive any information regarding your payment, and GitHub doesn't offer refunds, sponsorships are non-refundable. +1. If you cancel your sponsorship, GitHub schedules a cancellation request which will become effective at the end of the billing cycle, which ends at the 22nd of a month for monthly sponsorships. This means that even though you cancel your sponsorship, you will keep your access to Insiders as long as your cancellation isn't effective. All charges are processed by GitHub through Stripe. As we don't receive any information regarding your payment, and GitHub doesn't offer refunds, sponsorships are non-refundable. -2. It's currently not possible to grant access to each member of an organization, as GitHub only allows for adding users. Thus, after sponsoring, please send an email to sponsors@raspap.com, stating which account should become a collaborator of the Insiders repository. We're working on a solution which will make access to organizations much simpler. +2. It's currently not possible to grant access to each member of an organization, as GitHub only allows for adding users. Thus, after sponsoring, please send an email to sponsors@raspap.com, stating which account should become a collaborator of the Insiders repository. We're working on a solution which will make access to organizations much simpler. From 6e5d69affb8852f0fd32681bb648bd2722181f10 Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Fri, 5 Feb 2021 15:27:31 +0100 Subject: [PATCH 028/300] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ea78e8b89..085eb90d8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ ![](https://i.imgur.com/DpgvLIO.png) [![Release 2.6](https://img.shields.io/badge/release-v2.6-green)](https://github.com/raspap/raspap-webgui/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 strategy, 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*. +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. ## Contents - [How sponsorship works](#how-sponsorship-works) - - [About your sponsorship](#about-your-sponsorhip) + - [About your sponsorship](#about-your-sponsorship) - [Exclusive features](#exclusive-features) - [Funding targets](#funding-targets) - [Frequently asked questions](#frequently-asked-questions) @@ -42,7 +42,7 @@ Following is a list of funding targets. When a funding target is reached, the fe ### Terms *We're using RaspAP for a commercial project. Can we use Insiders under the same terms and conditions?* -Yes. Whether you're an individual or a company, you may use RaspAP Insiders precisely under the same terms as RaspAP, which are defined by the GNU GPL 3.0 license. However, we kindly ask you to respect the following guidelines: +Yes. Whether you're an individual or a company, you may use RaspAP Insiders precisely under the same terms as RaspAP, which are defined by the [GNU GPL-3.0 license](https://github.com/RaspAP/raspap-insiders/blob/master/LICENSE). However, we kindly ask you to respect the following guidelines: * Please **don't distribute the source code** of Insiders. You may freely use it for public, private or commercial projects, fork it, mirror it, do whatever you want with it, but please don't release the source code, as it would counteract the sponsorware strategy. * If you cancel your subscription, you're removed as a collaborator and will miss out on future updates of Insiders. However, you may *use the latest version* that's available to you as long as you like. Just remember that [GitHub deletes private forks](https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/removing-a-collaborator-from-a-personal-repository). From 932166b3da8f079fc000ee8f8d8d3bc63679dd4a Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Fri, 5 Feb 2021 20:09:34 +0100 Subject: [PATCH 029/300] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 085eb90d8..0eded8f98 100644 --- a/README.md +++ b/README.md @@ -17,19 +17,19 @@ New features first land in Insiders, which means that *sponsors will have access Don't want to sponsor? No problem, RaspAP already has tons of features available, so chances are that most of your requirements are already satisfied. See the list of [exclusive features](#exclusive-features) to learn which features are currently only available to sponsors. ## About your sponsorship -Your sponsorship is through your individual or organization's GitHub account. By visiting [**RaspAP's sponsor profile**](), you may change your sponsorship tier or cancel your sponsorship anytime. [1](#footnote-1) +Your sponsorship is through your individual or organization's GitHub account. By visiting [**RaspAP's sponsor profile**](https://github.com/sponsors/RaspAP), you may change your sponsorship tier or cancel your sponsorship anytime. [1](#footnote-1) As part of the initial rollout of Insiders, all previous one-time backers of RaspAP on GitHub, PayPal or [Open Collective](https://opencollective.com/raspap) will receive unlimited access to Insiders. This is a small gesture for those early financial supporters who recognized the potential of this project. -> ℹ️ **Important**: If you're sponsoring [RaspAP](https://github.com/RaspAP/sponsors) 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) +> ℹ️ **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. ✅ Manage OpenVPN client configs -We feel that using input from our Insiders to drive the development of this project is a great approach. 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](/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/orgs/RaspAP/teams/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. From fd625203555147b7a50e5abb3b73e096f8a6b74e Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 6 Feb 2021 08:18:27 +0000 Subject: [PATCH 030/300] Create openvpn subtemplates --- templates/openvpn.php | 87 +++++++++++------------------------ templates/openvpn/configs.php | 9 ++++ templates/openvpn/general.php | 37 +++++++++++++++ templates/openvpn/logging.php | 11 +++++ 4 files changed, 83 insertions(+), 61 deletions(-) create mode 100644 templates/openvpn/configs.php create mode 100644 templates/openvpn/general.php create mode 100644 templates/openvpn/logging.php diff --git a/templates/openvpn.php b/templates/openvpn.php index 65976deea..02cf6b18d 100755 --- a/templates/openvpn.php +++ b/templates/openvpn.php @@ -1,3 +1,15 @@ + + + + ' , PHP_EOL; + } else { + echo '' , PHP_EOL; + } + ?> + + +
@@ -21,70 +33,23 @@ +
-
-

-
-
-
-
-
-
-
-
-
-
- - -
-
-
-
- - -
-
-
-
-
- - -
-
-
-
-
- -
-
-
-
-

-
-
- '; - ?> -
-
-
- - - ' , PHP_EOL; - } else { - echo '' , PHP_EOL; - } - ?> - - -
+ + + +
+ + + +
- -
-
+ +
+
diff --git a/templates/openvpn/configs.php b/templates/openvpn/configs.php new file mode 100644 index 000000000..fd9fd3eff --- /dev/null +++ b/templates/openvpn/configs.php @@ -0,0 +1,9 @@ +
+

+
+
+
+
+
+ + diff --git a/templates/openvpn/general.php b/templates/openvpn/general.php new file mode 100644 index 000000000..ddc751e7d --- /dev/null +++ b/templates/openvpn/general.php @@ -0,0 +1,37 @@ +
+

+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ +
+
+
+ diff --git a/templates/openvpn/logging.php b/templates/openvpn/logging.php new file mode 100644 index 000000000..caba88628 --- /dev/null +++ b/templates/openvpn/logging.php @@ -0,0 +1,11 @@ +
+

+
+
+ '; + ?> +
+
+
+ From dc03d9ea002557f1c28a20b6849bb52437e25f6f Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 6 Feb 2021 11:03:30 +0000 Subject: [PATCH 031/300] Prepend .ovpn filename to client + login --- includes/functions.php | 37 +++++++++++++++++++++++++++++++++++++ includes/openvpn.php | 14 +++++++++++--- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/includes/functions.php b/includes/functions.php index f1dec2c0c..923ff007b 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -224,6 +224,43 @@ function safefilerewrite($fileName, $dataToSave) } } +/** + * Prepends data to a file if not exists + * + * @param string $filename + * @param string $dataToSave + * @return boolean + */ +function file_prepend_data($filename, $dataToSave) +{ + $context = stream_context_create(); + $file = fopen($filename, 'r', 1, $context); + $file_data = readfile($file); + + if (!preg_match('/^'.$dataToSave.'/', $file_data)) { + $tmp_file = tempnam(sys_get_temp_dir(), 'php_prepend_'); + file_put_contents($tmp_file, $dataToSave); + file_put_contents($tmp_file, $file, FILE_APPEND); + fclose($file); + unlink($filename); + rename($tmp_file, $filename); + return true; + } else { + return false; + } +} + +/** + * Callback function for array_filter + * + * @param string $var + * @return filtered value + */ +function filter_comments($var) +{ + return $var[0] != '#'; +} + /** * Saves a CSRF token in the session */ diff --git a/includes/openvpn.php b/includes/openvpn.php index 6f9e40b5b..d337f04da 100755 --- a/includes/openvpn.php +++ b/includes/openvpn.php @@ -47,8 +47,9 @@ function DisplayOpenVPNConfig() // parse client auth credentials if (!empty($auth)) { - $authUser = $auth[0]; - $authPassword = $auth[1]; + $auth = array_filter($auth, 'filter_comments'); + $authUser = current($auth); + $authPassword = next($auth); } echo renderTemplate( @@ -136,18 +137,25 @@ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword) ) { throw new RuntimeException('Unable to move uploaded file'); } + + // Good file upload, update auth credentials if present + $prepend = '# filename '.pathinfo($file['name'], PATHINFO_FILENAME) .PHP_EOL; if (!empty($authUser) && !empty($authPassword)) { $auth_flag = 1; // Move tmp authdata to /etc/openvpn/login.conf - $auth = $authUser .PHP_EOL . $authPassword .PHP_EOL; + $auth.= $authUser .PHP_EOL . $authPassword .PHP_EOL; file_put_contents($tmp_authdata, $auth); + file_prepend_data($tmp_authdata, $prepend); system("sudo cp $tmp_authdata " . RASPI_OPENVPN_CLIENT_LOGIN, $return); if ($return !=0) { $status->addMessage('Unable to save client auth credentials', 'danger'); } } + // Prepend filname tag to .ovpn client config + file_prepend_data($tmp_ovpnclient, $prepend); + // Set iptables rules and, optionally, auth-user-pass exec("sudo /etc/raspap/openvpn/configauth.sh $tmp_ovpnclient $auth_flag " .$_SESSION['ap_interface'], $return); foreach ($return as $line) { From 9bc9f2d3d74ffd47720dcfaa3e6dd58f9d547ac1 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Feb 2021 11:50:52 +0000 Subject: [PATCH 032/300] Add ajax delete handler --- ajax/openvpn/del_ovpncfg.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 ajax/openvpn/del_ovpncfg.php diff --git a/ajax/openvpn/del_ovpncfg.php b/ajax/openvpn/del_ovpncfg.php new file mode 100644 index 000000000..77adacff5 --- /dev/null +++ b/ajax/openvpn/del_ovpncfg.php @@ -0,0 +1,13 @@ +$return]; + echo json_encode($jsonData); +} + From f1b1e96df3959bc2eae1004539ae0b7219c5faf6 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Feb 2021 11:52:58 +0000 Subject: [PATCH 033/300] Progress commit: templates/openvpn --- templates/openvpn.php | 38 ++++++++++++++++++++++++++++++++++- templates/openvpn/configs.php | 32 ++++++++++++++++++++++++----- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/templates/openvpn.php b/templates/openvpn.php index 02cf6b18d..4f76d6ecb 100755 --- a/templates/openvpn.php +++ b/templates/openvpn.php @@ -33,7 +33,7 @@ @@ -53,3 +53,39 @@
+ + + + + + diff --git a/templates/openvpn/configs.php b/templates/openvpn/configs.php index fd9fd3eff..84d701d35 100644 --- a/templates/openvpn/configs.php +++ b/templates/openvpn/configs.php @@ -1,9 +1,31 @@
-

-
-
-
-
+

+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
From ef09dd0f6060dc426aefdb4d6c86f5701181c2b7 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Feb 2021 11:53:57 +0000 Subject: [PATCH 034/300] Add file utility functions --- includes/functions.php | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/includes/functions.php b/includes/functions.php index 923ff007b..7593df187 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -250,6 +250,47 @@ function file_prepend_data($filename, $dataToSave) } } +/** + * Fetches a meta value from a file + * + * @param string $filename + * @param string $pattern + * @return string + */ +function file_get_meta($filename, $pattern) +{ + if(file_exists($filename)) { + $context = stream_context_create(); + $file_data = file_get_contents($filename, false, $context); + preg_match('/^'.$pattern.'/', $file_data, $matched); + return $matched[1]; + } else { + return false; + } +} + +/** + * Renames an openvpn client config with the 'filename' header comment + * + * @param string file + * @return boolean + */ +function file_move_config($file) +{ + if(file_exists($file)) { + $file_data = file_get_contents($file); + preg_match('/^#\sfilename\s(.*)/i', $file_data, $matched); + $renamed = pathinfo($file, PATHINFO_DIRNAME).'/'. + $matched[1] .'_'.pathinfo($file, PATHINFO_FILENAME).'.'. + pathinfo($file, PATHINFO_EXTENSION); + if (!file_exists($renamed)) { + $return = system("sudo mv $file $renamed", $return); + } else { + return false; + } + } +} + /** * Callback function for array_filter * From f48e77da6c3dac71bbc8ef8cdb2b6278ffc8569a Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Feb 2021 11:54:57 +0000 Subject: [PATCH 035/300] Update w/ file_move_config + permissions --- includes/openvpn.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/includes/openvpn.php b/includes/openvpn.php index d337f04da..7b54d4de6 100755 --- a/includes/openvpn.php +++ b/includes/openvpn.php @@ -51,6 +51,7 @@ function DisplayOpenVPNConfig() $authUser = current($auth); $authPassword = next($auth); } + $clients = preg_grep('~\login.(conf)$~', scandir(pathinfo(RASPI_OPENVPN_CLIENT_LOGIN, PATHINFO_DIRNAME))); echo renderTemplate( "openvpn", compact( @@ -59,7 +60,8 @@ function DisplayOpenVPNConfig() "openvpnstatus", "public_ip", "authUser", - "authPassword" + "authPassword", + "clients" ) ); } @@ -147,6 +149,8 @@ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword) $auth.= $authUser .PHP_EOL . $authPassword .PHP_EOL; file_put_contents($tmp_authdata, $auth); file_prepend_data($tmp_authdata, $prepend); + file_move_config(RASPI_OPENVPN_CLIENT_LOGIN); + chmod($tmp_authdata, 0644); system("sudo cp $tmp_authdata " . RASPI_OPENVPN_CLIENT_LOGIN, $return); if ($return !=0) { $status->addMessage('Unable to save client auth credentials', 'danger'); @@ -163,6 +167,8 @@ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword) } // Copy tmp client config to /etc/openvpn/client + file_move_config(RASPI_OPENVPN_CLIENT_CONFIG); + chmod($tmp_ovpnclient, 0644); system("sudo cp $tmp_ovpnclient " . RASPI_OPENVPN_CLIENT_CONFIG, $return); if ($return ==0) { $status->addMessage('OpenVPN client.conf uploaded successfully', 'info'); From 56ca7ab281a35ab8ddec3c31e54099f94e648c6a Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Feb 2021 11:56:18 +0000 Subject: [PATCH 036/300] Update w/ openvpn client cfg actions --- installers/raspap.sudoers | 2 ++ 1 file changed, 2 insertions(+) diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index 00e1d1c7f..d141653e6 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -20,6 +20,8 @@ www-data ALL=(ALL) NOPASSWD:/bin/systemctl stop openvpn-client@client www-data ALL=(ALL) NOPASSWD:/bin/systemctl disable openvpn-client@client www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/ovpnclient.ovpn /etc/openvpn/client/client.conf www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/authdata /etc/openvpn/client/login.conf +www-data ALL=(ALL) NOPASSWD:/bin/mv /etc/openvpn/client/*.conf /etc/openvpn/client/*.conf +www-data ALL=(ALL) NOPASSWD:/bin/rm /etc/openvpn/client/*.conf www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/dnsmasqdata /etc/dnsmasq.d/090_*.conf www-data ALL=(ALL) NOPASSWD:/bin/rm /etc/dnsmasq.d/090_*.conf www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/dhcpddata /etc/dhcpcd.conf From 12dd5d824bded89c8187e693a610f64cb73269ab Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Feb 2021 11:59:02 +0000 Subject: [PATCH 037/300] Progress commit: bootstrap modal event handlers --- ajax/openvpn/activate_ovpncfg.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 ajax/openvpn/activate_ovpncfg.php diff --git a/ajax/openvpn/activate_ovpncfg.php b/ajax/openvpn/activate_ovpncfg.php new file mode 100644 index 000000000..d2a218975 --- /dev/null +++ b/ajax/openvpn/activate_ovpncfg.php @@ -0,0 +1,14 @@ +$return]; + echo json_encode($jsonData); +} + From 6432efcf34b88faa7094c612ba7a0603d939ab81 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Feb 2021 11:59:16 +0000 Subject: [PATCH 038/300] Progress commit: bootstrap modal event handlers --- app/js/custom.js | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/app/js/custom.js b/app/js/custom.js index 7a885472e..92c73eba6 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -248,6 +248,56 @@ $('#hostapdModal').on('shown.bs.modal', function (e) { $('#configureClientModal').on('shown.bs.modal', function (e) { }); +$('#ovpn-confirm-delete').on('click', '.btn-delete', function (e) { + var modalDiv = $(e.delegateTarget); + var cfg_id = $(this).data('recordId'); + console.log(cfg_id); + //console.log(modalDiv.parent().find('.js-remove-openvpn-client').attr('data-record-id')); + //console.log(modalDiv.parent().find( + + $.post('ajax/openvpn/del_ovpncfg.php',{'cfg_id':cfg_id},function(data){ + jsonData = JSON.parse(data); + console.log(jsonData); + //$(this).closest('js-openvpn-client-row').fadeOut(300); + $("#ovpn-confirm-delete").modal('hide'); + if(jsonData['return'] == 0) { + // do something + } else if(jsonData['return'] == 2) { + // something else + } + }); +}); + +$('#ovpn-confirm-delete').on('show.bs.modal', function (e) { + var data = $(e.relatedTarget).data(); + $('.btn-delete', this).data('recordId', data.recordId); +}); + +$('#ovpn-confirm-activate').on('click', '.btn-activate', function (e) { + //var modalDiv = $(e.delegateTarget); + var cfg_id = $(this).data('recordId'); + console.log(cfg_id); + + $.post('ajax/openvpn/activate_ovpncfg.php',{'cfg_id':cfg_id},function(data){ + jsonData = JSON.parse(data); + console.log(jsonData); + //$(this).closest('js-openvpn-client-row').fadeOut(300); + $("#ovpn-confirm-activate").modal('hide'); + if(jsonData['return'] == 0) { + // do something + } else if(jsonData['return'] == 2) { + // something else + } + }); +}); + +$('#ovpn-confirm-activate').on('shown.bs.modal', function (e) { + var data = $(e.relatedTarget).data(); + console.log(data.recordId); + $('.btn-activate', this).data('recordId', data.recordId); +}); + + /* Sets the wirelss channel select options based on hw_mode and country_code. From dc1e4b4bc7bd51eb46b34d7c14365a8a7b7213c6 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Feb 2021 13:04:04 +0000 Subject: [PATCH 039/300] Swap profiles + restart openvpn-client --- ajax/openvpn/activate_ovpncfg.php | 32 ++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/ajax/openvpn/activate_ovpncfg.php b/ajax/openvpn/activate_ovpncfg.php index d2a218975..2f1e808d6 100644 --- a/ajax/openvpn/activate_ovpncfg.php +++ b/ajax/openvpn/activate_ovpncfg.php @@ -1,13 +1,39 @@ addMessage('Attempting to restart OpenVPN', 'info'); + exec('sudo /bin/systemctl stop openvpn-client@client', $return); + exec('sudo /bin/systemctl enable openvpn-client@client', $return); + exec('sudo /bin/systemctl start openvpn-client@client', $return); + + foreach ($return as $line) { + $status->addMessage($line, 'info'); + } + $return = $status; $jsonData = ['return'=>$return]; echo json_encode($jsonData); } From ee1da31da0d6ca3826b257b3c00559187ce75ca3 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Feb 2021 13:50:55 +0000 Subject: [PATCH 040/300] Code cleanup --- ajax/openvpn/activate_ovpncfg.php | 19 ++++++------------- app/js/custom.js | 14 +++----------- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/ajax/openvpn/activate_ovpncfg.php b/ajax/openvpn/activate_ovpncfg.php index 2f1e808d6..b8f822c28 100644 --- a/ajax/openvpn/activate_ovpncfg.php +++ b/ajax/openvpn/activate_ovpncfg.php @@ -1,12 +1,9 @@ addMessage('Attempting to restart OpenVPN', 'info'); - exec('sudo /bin/systemctl stop openvpn-client@client', $return); - exec('sudo /bin/systemctl enable openvpn-client@client', $return); - exec('sudo /bin/systemctl start openvpn-client@client', $return); + exec("sudo /bin/systemctl stop openvpn-client@client", $return); + sleep(1); + exec("sudo /bin/systemctl enable openvpn-client@client", $return); + sleep(1); + exec("sudo /bin/systemctl start openvpn-client@client", $return); - foreach ($return as $line) { - $status->addMessage($line, 'info'); - } - $return = $status; - $jsonData = ['return'=>$return]; - echo json_encode($jsonData); + echo json_encode($return); } diff --git a/app/js/custom.js b/app/js/custom.js index 92c73eba6..b1f445cef 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -274,26 +274,18 @@ $('#ovpn-confirm-delete').on('show.bs.modal', function (e) { }); $('#ovpn-confirm-activate').on('click', '.btn-activate', function (e) { - //var modalDiv = $(e.delegateTarget); var cfg_id = $(this).data('recordId'); - console.log(cfg_id); - $.post('ajax/openvpn/activate_ovpncfg.php',{'cfg_id':cfg_id},function(data){ jsonData = JSON.parse(data); - console.log(jsonData); - //$(this).closest('js-openvpn-client-row').fadeOut(300); $("#ovpn-confirm-activate").modal('hide'); - if(jsonData['return'] == 0) { - // do something - } else if(jsonData['return'] == 2) { - // something else - } + setTimeout(function(){ + window.location.reload(); + },300); }); }); $('#ovpn-confirm-activate').on('shown.bs.modal', function (e) { var data = $(e.relatedTarget).data(); - console.log(data.recordId); $('.btn-activate', this).data('recordId', data.recordId); }); From 361a37332a63eca33bd43cc58d9801c5e27bd0ae Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Feb 2021 15:24:45 +0000 Subject: [PATCH 041/300] Update en_US locale + template --- locale/en_US/LC_MESSAGES/messages.mo | Bin 17365 -> 18271 bytes locale/en_US/LC_MESSAGES/messages.po | 27 ++++++++++++ templates/openvpn/configs.php | 63 +++++++++++++++------------ 3 files changed, 61 insertions(+), 29 deletions(-) diff --git a/locale/en_US/LC_MESSAGES/messages.mo b/locale/en_US/LC_MESSAGES/messages.mo index 34d53bd3567d76aca246ffff641444300801299f..451dd153e80dabdc3f40fdae6f029c35256d88ea 100644 GIT binary patch literal 18271 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 U>&WESk^H;G1Ya}$|9&0$ZznK{ng9R* literal 17365 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 S_{Dw0pVR6W_5U9i_x}x#pMUQF diff --git a/locale/en_US/LC_MESSAGES/messages.po b/locale/en_US/LC_MESSAGES/messages.po index 8c4d5f975..b212270d8 100644 --- a/locale/en_US/LC_MESSAGES/messages.po +++ b/locale/en_US/LC_MESSAGES/messages.po @@ -677,6 +677,33 @@ msgstr "Attempting to start openvpn" msgid "Attempting to stop openvpn" msgstr "Attempting to stop openvpn" +msgid "Configurations" +msgstr "Configurations" + +msgid "Currently available OpenVPN client configurations are displayed below." +msgstr "Currently available OpenVPN client configurations are displayed below." + +msgid "Activating a configuraton will restart the openvpn-client service." +msgstr "Activating a configuraton will restart the openvpn-client service." + +msgid "Delete OpenVPN client" +msgstr "Delete OpenVPN client" + +msgid "Delete client configuration? This cannot be undone." +msgstr "Delete client configuration? This cannot be undone." + +msgid "Activate OpenVPN client" +msgstr "Activate OpenVPN client" + +msgid "Activate client configuration? This will restart the openvpn-client service." +msgstr "Activate client configuration? This will restart the openvpn-client service." + +msgid "Activate" +msgstr "Activate" + +msgid "Cancel" +msgstr "Cancel" + #: includes/torproxy.php msgid "TOR is not running" msgstr "TOR is not running" diff --git a/templates/openvpn/configs.php b/templates/openvpn/configs.php index 84d701d35..1839c6759 100644 --- a/templates/openvpn/configs.php +++ b/templates/openvpn/configs.php @@ -1,31 +1,36 @@
-

-
- -
-
- -
-
- -
-
- -
-
- -
-
+
+
+

+

+ +
openvpn-client service.") ?> +

+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
-
- - +
From c073d18133a4b103d1f83129ad1a8877d8433ddd Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Feb 2021 18:51:21 +0000 Subject: [PATCH 042/300] Cleanup + fadeOut deleted cfg --- app/js/custom.js | 16 ++++------------ templates/openvpn/configs.php | 31 +++++++++++++++---------------- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/app/js/custom.js b/app/js/custom.js index b1f445cef..dcb057974 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -249,22 +249,14 @@ $('#configureClientModal').on('shown.bs.modal', function (e) { }); $('#ovpn-confirm-delete').on('click', '.btn-delete', function (e) { - var modalDiv = $(e.delegateTarget); var cfg_id = $(this).data('recordId'); - console.log(cfg_id); - //console.log(modalDiv.parent().find('.js-remove-openvpn-client').attr('data-record-id')); - //console.log(modalDiv.parent().find( - $.post('ajax/openvpn/del_ovpncfg.php',{'cfg_id':cfg_id},function(data){ jsonData = JSON.parse(data); - console.log(jsonData); - //$(this).closest('js-openvpn-client-row').fadeOut(300); $("#ovpn-confirm-delete").modal('hide'); - if(jsonData['return'] == 0) { - // do something - } else if(jsonData['return'] == 2) { - // something else - } + var row = $(document.getElementById("openvpn-client-row-" + cfg_id)); + row.fadeOut( "slow", function() { + row.remove(); + }); }); }); diff --git a/templates/openvpn/configs.php b/templates/openvpn/configs.php index 1839c6759..6f8056fd2 100644 --- a/templates/openvpn/configs.php +++ b/templates/openvpn/configs.php @@ -1,33 +1,32 @@
-
+


openvpn-client service.") ?>

- -
-
+ +
+
-
+
-
+
-
+
From 33e5f791c75c778781b184c31ec9605f1b9cd956 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 8 Feb 2021 07:09:50 +0000 Subject: [PATCH 043/300] Update openvpn template --- app/img/180x150.png | Bin 38125 -> 0 bytes app/img/no-trace-200x200.png | Bin 0 -> 33043 bytes templates/openvpn.php | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 app/img/180x150.png create mode 100644 app/img/no-trace-200x200.png diff --git a/app/img/180x150.png b/app/img/180x150.png deleted file mode 100644 index 975230ef0d692b3543085e781b31cb0ec103ba2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38125 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 bPk;dcU&?OuRm`WA00000NkvXXu0mjfm`iWk literal 0 HcmV?d00001 diff --git a/templates/openvpn.php b/templates/openvpn.php index 65976deea..df97ce32b 100755 --- a/templates/openvpn.php +++ b/templates/openvpn.php @@ -57,7 +57,7 @@

- +
From 76a997049b8341a0bc048d9cb314b3e11134fb8f Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 8 Feb 2021 07:12:31 +0000 Subject: [PATCH 044/300] Revert "Update openvpn template" This reverts commit 33e5f791c75c778781b184c31ec9605f1b9cd956. --- app/img/180x150.png | Bin 0 -> 38125 bytes app/img/no-trace-200x200.png | Bin 33043 -> 0 bytes templates/openvpn.php | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 app/img/180x150.png delete mode 100644 app/img/no-trace-200x200.png diff --git a/app/img/180x150.png b/app/img/180x150.png new file mode 100644 index 0000000000000000000000000000000000000000..975230ef0d692b3543085e781b31cb0ec103ba2e GIT binary patch literal 38125 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 bPk;dcU&?OuRm`WA00000NkvXXu0mjfm`iWk diff --git a/templates/openvpn.php b/templates/openvpn.php index df97ce32b..65976deea 100755 --- a/templates/openvpn.php +++ b/templates/openvpn.php @@ -57,7 +57,7 @@

- +
From 04edc3a185315108565ff495cb2d3cae5c99a3b5 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 9 Feb 2021 21:57:15 +0000 Subject: [PATCH 045/300] Implement openvpn logging --- includes/openvpn.php | 12 +++++++++++- installers/openvpnlog.sh | 3 +++ installers/raspap.sudoers | 1 + templates/openvpn/logging.php | 19 ++++++++++++------- 4 files changed, 27 insertions(+), 8 deletions(-) create mode 100755 installers/openvpnlog.sh diff --git a/includes/openvpn.php b/includes/openvpn.php index 7b54d4de6..16954d23f 100755 --- a/includes/openvpn.php +++ b/includes/openvpn.php @@ -20,7 +20,9 @@ function DisplayOpenVPNConfig() if (isset($_POST['authPassword'])) { $authPassword = strip_tags(trim($_POST['authPassword'])); } - $return = SaveOpenVPNConfig($status, $_FILES['customFile'], $authUser, $authPassword); + if (is_uploaded_file( $_FILES["customFile"]["tmp_name"])) { + $return = SaveOpenVPNConfig($status, $_FILES['customFile'], $authUser, $authPassword); + } } elseif (isset($_POST['StartOpenVPN'])) { $status->addMessage('Attempting to start OpenVPN', 'info'); exec('sudo /bin/systemctl start openvpn-client@client', $return); @@ -53,11 +55,19 @@ function DisplayOpenVPNConfig() } $clients = preg_grep('~\login.(conf)$~', scandir(pathinfo(RASPI_OPENVPN_CLIENT_LOGIN, PATHINFO_DIRNAME))); + if (isset($_POST['log-openvpn'])) { + $logEnable = 1; + exec("sudo /etc/raspap/openvpn/openvpnlog.sh", $logOutput); + $logOutput = file_get_contents('/tmp/openvpn.log'); + } + echo renderTemplate( "openvpn", compact( "status", "serviceStatus", "openvpnstatus", + "logEnable", + "logOutput", "public_ip", "authUser", "authPassword", diff --git a/installers/openvpnlog.sh b/installers/openvpnlog.sh new file mode 100755 index 000000000..3e2d681c1 --- /dev/null +++ b/installers/openvpnlog.sh @@ -0,0 +1,3 @@ +#!/bin/bash +touch /tmp/openvpn.log +grep -m 50 openvpn /var/log/syslog | sudo tee /tmp/openvpn.log diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index d141653e6..49d4032bf 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -36,6 +36,7 @@ www-data ALL=(ALL) NOPASSWD:/etc/raspap/hostapd/disablelog.sh www-data ALL=(ALL) NOPASSWD:/etc/raspap/hostapd/servicestart.sh www-data ALL=(ALL) NOPASSWD:/etc/raspap/lighttpd/configport.sh 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/cp /tmp/dnsmasqdata /etc/dnsmasq.d/090_adblock.conf diff --git a/templates/openvpn/logging.php b/templates/openvpn/logging.php index caba88628..4d44cece3 100644 --- a/templates/openvpn/logging.php +++ b/templates/openvpn/logging.php @@ -1,11 +1,16 @@ +
-

+

+

openvpn activity.") ?>

+ +
+ aria-describedby="log-openvpn"> + +
-
- '; - ?> +
+
-
-
+
+ From 0ffe0ecd4b7481b412b0c8014a29b1e4c29bb3c9 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 10 Feb 2021 11:27:24 +0000 Subject: [PATCH 046/300] Persist log-openvpn option --- includes/openvpn.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/includes/openvpn.php b/includes/openvpn.php index 16954d23f..810746a29 100755 --- a/includes/openvpn.php +++ b/includes/openvpn.php @@ -55,7 +55,15 @@ function DisplayOpenVPNConfig() } $clients = preg_grep('~\login.(conf)$~', scandir(pathinfo(RASPI_OPENVPN_CLIENT_LOGIN, PATHINFO_DIRNAME))); - if (isset($_POST['log-openvpn'])) { + $logEnable = 0; + if (!empty($_POST) && !isset($_POST['log-openvpn'])) { + $logOutput = ""; + $f = @fopen("/tmp/openvpn.log", "r+"); + if ($f !== false) { + ftruncate($f, 0); + fclose($f); + } + } elseif (isset($_POST['log-openvpn']) || filesize('/tmp/openvpn.log') >0) { $logEnable = 1; exec("sudo /etc/raspap/openvpn/openvpnlog.sh", $logOutput); $logOutput = file_get_contents('/tmp/openvpn.log'); From 7798e710c1edc8852e59c5d170036be6a916d4f8 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 10 Feb 2021 11:28:33 +0000 Subject: [PATCH 047/300] Install openvpn logging script --- installers/common.sh | 5 +++-- installers/openvpnlog.sh | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/installers/common.sh b/installers/common.sh index 9f9c9d877..7edad9c6d 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -313,9 +313,10 @@ function _create_openvpn_scripts() { _install_log "Creating OpenVPN control scripts" sudo mkdir $raspap_dir/openvpn || _install_status 1 "Unable to create directory '$raspap_dir/openvpn'" - # Move service auth control shell scripts + # Move service auth control & logging shell scripts sudo cp "$webroot_dir/installers/"configauth.sh "$raspap_dir/openvpn" || _install_status 1 "Unable to move auth control script" - # Make configauth.sh writable by www-data group + sudo cp "$webroot_dir/installers/"openvpnlog.sh "$raspap_dir/openvpn" || _install_status 1 "Unable to move logging script" + # Make scripts executable by www-data group sudo chown -c root:"$raspap_user" "$raspap_dir/openvpn/"*.sh || _install_status 1 "Unable change owner and/or group" sudo chmod 750 "$raspap_dir/openvpn/"*.sh || _install_status 1 "Unable to change file permissions" _install_status 0 diff --git a/installers/openvpnlog.sh b/installers/openvpnlog.sh index 3e2d681c1..96e79e2dc 100755 --- a/installers/openvpnlog.sh +++ b/installers/openvpnlog.sh @@ -1,3 +1,3 @@ #!/bin/bash touch /tmp/openvpn.log -grep -m 50 openvpn /var/log/syslog | sudo tee /tmp/openvpn.log +grep -m 100 openvpn /var/log/syslog | sudo tee /tmp/openvpn.log From 76dc60ca4162d68f26f307b81f8be018e7f9fab7 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 10 Feb 2021 11:29:12 +0000 Subject: [PATCH 048/300] Update logging template --- templates/openvpn/logging.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/openvpn/logging.php b/templates/openvpn/logging.php index 4d44cece3..0aef42170 100644 --- a/templates/openvpn/logging.php +++ b/templates/openvpn/logging.php @@ -4,7 +4,7 @@

openvpn activity.") ?>

- aria-describedby="log-openvpn"> + aria-describedby="log-openvpn">
From 196e17bfd68a469806f2a0697f0bd3fbc150c682 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 10 Feb 2021 11:29:34 +0000 Subject: [PATCH 049/300] Update en_US locale --- locale/en_US/LC_MESSAGES/messages.po | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/locale/en_US/LC_MESSAGES/messages.po b/locale/en_US/LC_MESSAGES/messages.po index b212270d8..dcd715ae4 100644 --- a/locale/en_US/LC_MESSAGES/messages.po +++ b/locale/en_US/LC_MESSAGES/messages.po @@ -704,6 +704,15 @@ msgstr "Activate" msgid "Cancel" 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" From 0f7ff60a81d3fe9922699f5ab88eb04eccb3b718 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 10 Feb 2021 16:20:43 +0000 Subject: [PATCH 050/300] Update about page --- app/img/insiders.png | Bin 0 -> 4122 bytes templates/about.php | 4 ++-- templates/about/general.php | 6 +++--- templates/about/{sponsors.php => insiders.php} | 0 4 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 app/img/insiders.png rename templates/about/{sponsors.php => insiders.php} (100%) diff --git a/app/img/insiders.png b/app/img/insiders.png new file mode 100644 index 0000000000000000000000000000000000000000..b29574f747ee8e91a9331bdf14cd7b36554bc086 GIT binary patch literal 4122 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 literal 0 HcmV?d00001 diff --git a/templates/about.php b/templates/about.php index 2d108da21..80ac2dd1d 100755 --- a/templates/about.php +++ b/templates/about.php @@ -18,14 +18,14 @@
- +
diff --git a/templates/about/general.php b/templates/about/general.php index db16a9ef0..df44bc779 100644 --- a/templates/about/general.php +++ b/templates/about/general.php @@ -7,12 +7,12 @@
RaspAP is a co-creation of billz and SirLagz with the contributions of our developer community and language translators. - Learn more about joining the project as a code contributor, - translator or financial sponsor.
+ Learn more about joining the project as a code contributor, + translator or financial sponsor with immediate access to exclusive features available to Insiders.
Project documentation is available at https://docs.raspap.com/
- From b7ed2960c129b56e8f52d41d02d41011e69a93fc Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 24 Feb 2021 10:32:07 +0000 Subject: [PATCH 074/300] Update wg-keygen for server + peer --- app/js/custom.js | 15 +++++++-------- templates/wg/general.php | 2 +- templates/wg/peers.php | 4 ++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/js/custom.js b/app/js/custom.js index e2c89ac61..e0bb61348 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -350,16 +350,15 @@ function clearBlocklistStatus() { } // Handler for the wireguard generate key button -function generateWgKey() { - var entity = $('#wg-srvpubkey').attr('name'); - console.log(entity); - $.post('ajax/networking/get_wgkey.php',{'entity':entity },function(data){ +$('.wg-keygen').click(function(){ + var entity = $(this).parent('div').prev('input[type="text"]'); + var updated = entity.attr('name')+"-pubkey-status"; + $.post('ajax/networking/get_wgkey.php',{'entity':entity.attr('name') },function(data){ var jsonData = JSON.parse(data); - console.log(jsonData); - $('#wg-srvpubkey').val(jsonData); - $('#wg-server-pubkey-status').removeClass('check-hidden').addClass('check-updated').delay(500).animate({ opacity: 1 }, 700); + entity.val(jsonData); + $('#' + updated).removeClass('check-hidden').addClass('check-updated').delay(500).animate({ opacity: 1 }, 700); }) -} +}) // Static Array method Array.range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start); diff --git a/templates/wg/general.php b/templates/wg/general.php index 3be32e45f..5f5417b16 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -27,7 +27,7 @@
- +
diff --git a/templates/wg/peers.php b/templates/wg/peers.php index 2b70680ab..e2ff5c6f0 100644 --- a/templates/wg/peers.php +++ b/templates/wg/peers.php @@ -41,8 +41,8 @@
- - + +
From 9bbf698b6a637a20b6d5a260c0765c328c5e5842 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 24 Feb 2021 18:06:18 +0000 Subject: [PATCH 075/300] Reorder template fields --- templates/wg/general.php | 17 +++++++++-------- templates/wg/peers.php | 29 +++++++++++++++-------------- templates/wireguard.php | 2 +- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/templates/wg/general.php b/templates/wg/general.php index 5f5417b16..3d0cb952e 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -13,12 +13,6 @@

-
-
- - -
-
@@ -30,14 +24,21 @@
+ +
+ + +
+
+ +
-
- +
diff --git a/templates/wg/peers.php b/templates/wg/peers.php index e2ff5c6f0..98298e37d 100644 --- a/templates/wg/peers.php +++ b/templates/wg/peers.php @@ -13,6 +13,20 @@ +
+
+ +
+
+ +
+ + +
+
+
+ +
@@ -34,20 +48,7 @@
-
-
- -
-
- -
- - -
-
-
- - + diff --git a/templates/wireguard.php b/templates/wireguard.php index 3ead09c7a..ee28dfa17 100644 --- a/templates/wireguard.php +++ b/templates/wireguard.php @@ -27,7 +27,7 @@
showMessages(); ?> -
+
+ From 6b002e3d4cef1b4cb2eafe3047a2f77186cf3e4a Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 4 Mar 2021 23:23:05 +0000 Subject: [PATCH 084/300] Update wg placeholder values --- config/defaults.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/defaults.json b/config/defaults.json index 46fce72c5..f87223cf9 100644 --- a/config/defaults.json +++ b/config/defaults.json @@ -36,12 +36,12 @@ }, "wireguard": { "server": { - "Address": [ "10.3.141.1/24" ], + "Address": [ "10.253.3.1/24" ], "ListenPort": [ "51820" ], "DNS": [ "10.3.141.1" ] }, "peer": { - "Endpoint": [ "server ip:53" ], + "Endpoint": [ "10.253.3.1/24:51820" ], "AllowedIPs": ["0.0.0.0/0"], "PersistentKeepalive": [ "15" ] } From 4515ac95fb4f9ace712fc99410054ff455f7a180 Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 4 Mar 2021 23:44:45 +0000 Subject: [PATCH 085/300] Improved private key handling (security) --- ajax/networking/get_wgkey.php | 1 - app/js/custom.js | 1 - includes/wireguard.php | 8 ++++++-- templates/wg/general.php | 1 - templates/wg/peers.php | 1 - 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ajax/networking/get_wgkey.php b/ajax/networking/get_wgkey.php index d3f55aac2..840d59f07 100644 --- a/ajax/networking/get_wgkey.php +++ b/ajax/networking/get_wgkey.php @@ -15,7 +15,6 @@ exec("sudo wg genkey | tee $privkey_tmp | wg pubkey > $pubkey_tmp", $return); $wgdata['pubkey'] = str_replace("\n",'',file_get_contents($pubkey_tmp)); - $wgdata['privkey'] = str_replace("\n",'',file_get_contents($privkey_tmp)); exec("sudo mv $privkey_tmp $privkey", $return); exec("sudo mv $pubkey_tmp $pubkey", $return); diff --git a/app/js/custom.js b/app/js/custom.js index 89c500bce..25260e812 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -357,7 +357,6 @@ $('.wg-keygen').click(function(){ $.post('ajax/networking/get_wgkey.php',{'entity':entity_pub.attr('name') },function(data){ var jsonData = JSON.parse(data); entity_pub.val(jsonData.pubkey); - entity_priv.val(jsonData.privkey); $('#' + updated).removeClass('check-hidden').addClass('check-updated').delay(500).animate({ opacity: 1 }, 700); }) }) diff --git a/includes/wireguard.php b/includes/wireguard.php index a9ade3d0a..41b67c7fa 100644 --- a/includes/wireguard.php +++ b/includes/wireguard.php @@ -110,11 +110,15 @@ function SaveWireGuardConfig($status) } // Save settings if ($good_input) { + // fetch private keys from filesytem + $wg_srvprivkey = exec('sudo cat '. RASPI_WIREGUARD_PATH .'wg-server-private.key', $return); + $wg_peerprivkey = exec('sudo cat '. RASPI_WIREGUARD_PATH .'wg-peer-private.key', $return); + // server (wg0.conf) $config[] = '[Interface]'; $config[] = 'Address = '.$_POST['wg_srvipaddress']; $config[] = 'ListenPort = '.$_POST['wg_srvport']; - $config[] = 'PrivateKey = '.$_POST['wg_srvprivkey']; + $config[] = 'PrivateKey = '.$wg_srvprivkey; $config[] = 'PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE'; $config[] = 'PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o wlan0 -j MASQUERADE'; $config[] = ''; @@ -139,7 +143,7 @@ function SaveWireGuardConfig($status) if ($_POST['wg_pendpoint'] !== '') { $config[] = 'Address = '.trim($_POST['wg_pendpoint']); } - $config[] = 'PrivateKey = '.$_POST['wg_peerprivkey']; + $config[] = 'PrivateKey = '.$wg_peerprivkey; $config[] = ''; $config[] = '[Peer]'; $config[] = 'PublicKey = '.$_POST['wg-server']; diff --git a/templates/wg/general.php b/templates/wg/general.php index 9ff217d27..db00ab21f 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -24,7 +24,6 @@ - diff --git a/templates/wg/peers.php b/templates/wg/peers.php index ae60a9735..cbdad1282 100644 --- a/templates/wg/peers.php +++ b/templates/wg/peers.php @@ -22,7 +22,6 @@ - From 0e89de206659ae66f5c60b9a568fb296f159f205 Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 4 Mar 2021 23:50:16 +0000 Subject: [PATCH 086/300] Remove private keys from payload --- includes/wireguard.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/includes/wireguard.php b/includes/wireguard.php index 41b67c7fa..0707efbbf 100644 --- a/includes/wireguard.php +++ b/includes/wireguard.php @@ -31,8 +31,6 @@ function DisplayWireGuardConfig() exec('sudo cat '. RASPI_WIREGUARD_CONFIG, $return); $conf = ParseConfig($return); $wg_srvpubkey = exec('sudo cat '. RASPI_WIREGUARD_PATH .'wg-server-public.key', $return); - $wg_srvprivkey = exec('sudo cat '. RASPI_WIREGUARD_PATH .'wg-server-private.key', $return); - $wg_peerprivkey = exec('sudo cat '. RASPI_WIREGUARD_PATH .'wg-peer-private.key', $return); $wg_srvport = ($conf['ListenPort'] == '') ? getDefaultNetValue('wireguard','server','ListenPort') : $conf['ListenPort']; $wg_srvipaddress = ($conf['Address'] == '') ? getDefaultNetValue('wireguard','server','Address') : $conf['Address']; $wg_pendpoint = ($conf['Endpoint'] == '') ? getDefaultNetValue('wireguard','peer','Endpoint') : $conf['Endpoint']; @@ -54,8 +52,6 @@ function DisplayWireGuardConfig() "endpoint_enable", "peer_id", "wg_srvpubkey", - "wg_srvprivkey", - "wg_peerprivkey", "wg_srvport", "wg_srvipaddress", "wg_peerpubkey", From 18729edd1e750f078e64878e7d2d5b8d41d1d9cb Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 4 Mar 2021 23:54:21 +0000 Subject: [PATCH 087/300] Update wg endpoint default value --- config/defaults.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/defaults.json b/config/defaults.json index f87223cf9..3c584ed03 100644 --- a/config/defaults.json +++ b/config/defaults.json @@ -41,7 +41,7 @@ "DNS": [ "10.3.141.1" ] }, "peer": { - "Endpoint": [ "10.253.3.1/24:51820" ], + "Endpoint": [ "10.253.3.1:51820" ], "AllowedIPs": ["0.0.0.0/0"], "PersistentKeepalive": [ "15" ] } From 333d447c6bfbe9b8767fa6ce3729e75ea6abefd0 Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 5 Mar 2021 08:32:00 +0000 Subject: [PATCH 088/300] Add defaults for wg server PostUp/Down --- config/defaults.json | 6 ++++-- includes/wireguard.php | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/config/defaults.json b/config/defaults.json index 3c584ed03..55dd0938d 100644 --- a/config/defaults.json +++ b/config/defaults.json @@ -38,10 +38,12 @@ "server": { "Address": [ "10.253.3.1/24" ], "ListenPort": [ "51820" ], - "DNS": [ "10.3.141.1" ] + "DNS": [ "10.3.141.1" ], + "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": { - "Endpoint": [ "10.253.3.1:51820" ], + "Endpoint": [ "10.253.3.1" ], "AllowedIPs": ["0.0.0.0/0"], "PersistentKeepalive": [ "15" ] } diff --git a/includes/wireguard.php b/includes/wireguard.php index 0707efbbf..63b268270 100644 --- a/includes/wireguard.php +++ b/includes/wireguard.php @@ -87,12 +87,12 @@ function SaveWireGuardConfig($status) } } if (isset($_POST['wg_pendpoint']) && strlen(trim($_POST['wg_pendpoint']) >0 )) { - if (!validateCidr($_POST['wg_pendpoint'])) { + if (!filter_var($_POST['wg_pendpoint'],FILTER_VALIDATE_IP)) { $status->addMessage('Invalid value for endpoint address', 'danger'); $good_input = false; } } - if (isset($_POST['wg_pallowedips'])) { + 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; @@ -115,13 +115,13 @@ function SaveWireGuardConfig($status) $config[] = 'Address = '.$_POST['wg_srvipaddress']; $config[] = 'ListenPort = '.$_POST['wg_srvport']; $config[] = 'PrivateKey = '.$wg_srvprivkey; - $config[] = 'PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE'; - $config[] = 'PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o wlan0 -j MASQUERADE'; + $config[] = 'PostUp = '.getDefaultNetValue('wireguard','server','PostUp'); + $config[] = 'PostDown = '.getDefaultNetValue('wireguard','server','PostDown'); $config[] = ''; $config[] = '[Peer]'; $config[] = 'PublicKey = '.$_POST['wg-peer']; if ($_POST['wg_pendpoint'] !== '') { - $config[] = 'Endpoint = '.trim($_POST['wg_pendpoint']); + $config[] = 'Endpoint = '.trim($_POST['wg_pendpoint']).':'.$_POST['wg_srvport']; } $config[] = 'AllowedIPs = '.$_POST['wg_pallowedips']; if ($_POST['wg_pkeepalive'] !== '') { From 406ff39ef8c8cf16baa8a42c4221eb69a46413ce Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 5 Mar 2021 19:24:18 +0000 Subject: [PATCH 089/300] Update release version --- BACKERS.md | 2 ++ README.md | 2 +- includes/defaults.php | 2 +- index.php | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/BACKERS.md b/BACKERS.md index 8ea2c184a..cabd988b4 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -21,6 +21,7 @@ When backers were asked which feature they'd most like to see added to RaspAP, t ✅ OpenVPN service logging ✅ Night mode toggle ✅ Restrict network to static clients +⚙️ WireGuard support (in progress) ⚙️ Traffic shaping (in progress) Look for the list above to grow as we add more exclusive features. @@ -33,6 +34,7 @@ Below is a list of funding targets. When a funding target is reached, the featur ✅ OpenVPN service logging ✅ Night mode toggle ✅ Restrict network to static clients +⚙️ WireGuard support (in progress) ⚙️ Traffic shaping (in progress) ### Frequently asked questions diff --git a/README.md b/README.md index 42266b4bf..ce80cb0bb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![](https://i.imgur.com/DpgvLIO.png) -[![Release 2.7](https://img.shields.io/badge/release-v2.7-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/) +[![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/) 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. diff --git a/includes/defaults.php b/includes/defaults.php index 93aa2df3b..3cde7d64b 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -6,7 +6,7 @@ $defaults = [ 'RASPI_BRAND_TEXT' => 'RaspAP', - 'RASPI_VERSION' => '2.7', + 'RASPI_VERSION' => '2.7.1', 'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json', 'RASPI_ADMIN_DETAILS' => RASPI_CONFIG.'/raspap.auth', 'RASPI_WIFI_AP_INTERFACE' => 'wlan0', diff --git a/index.php b/index.php index a132db68b..7e157bfe6 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 + * @version 2.7.1 * @link https://github.com/raspap/raspap-insiders/ * @link https://raspap.com/ * @see http://sirlagz.net/2013/02/08/raspap-webgui/ From c10d13c45e786d275da73eb68d4aea55d357ca5d Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Fri, 5 Mar 2021 20:30:03 +0100 Subject: [PATCH 090/300] Update README.md --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ce80cb0bb..901dd74d3 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,9 @@ When backers were asked which feature they'd most like to see added to RaspAP, t ✅ Manage OpenVPN client configs ✅ OpenVPN service logging ✅ Night mode toggle -⚙️ Traffic shaping (in progress) +✅ Restrict network to static clients +⚙️ WireGuard support (in progress) +⚙️ 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! @@ -41,7 +43,9 @@ Following is a list of funding targets. When a funding target is reached, the fe ✅ Manage OpenVPN client configs ✅ OpenVPN service logging ✅ Night mode toggle -⚙️ Traffic shaping (in progress) +✅ Restrict network to static clients +⚙️ WireGuard support (in progress) +⚙️ Traffic shaping (in progress) ## Frequently asked questions From 5d13d75ce7a927fdb3799d1d3ce9c089ce28026a Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Fri, 5 Mar 2021 20:30:47 +0100 Subject: [PATCH 091/300] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 901dd74d3..1f137ad18 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,9 @@ When backers were asked which feature they'd most like to see added to RaspAP, t ✅ Manage OpenVPN client configs ✅ OpenVPN service logging ✅ Night mode toggle -✅ Restrict network to static clients -⚙️ WireGuard support (in progress) -⚙️ Traffic shaping (in progress) +✅ Restrict network to static clients +⚙️ WireGuard support (in progress) +⚙️ 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! @@ -43,8 +43,8 @@ Following is a list of funding targets. When a funding target is reached, the fe ✅ Manage OpenVPN client configs ✅ OpenVPN service logging ✅ Night mode toggle -✅ Restrict network to static clients -⚙️ WireGuard support (in progress) +✅ Restrict network to static clients +⚙️ WireGuard support (in progress) ⚙️ Traffic shaping (in progress) ## Frequently asked questions From 6d635a96d52ec7eae2de184a6b382c459293e3e0 Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Sat, 6 Mar 2021 08:44:52 +0100 Subject: [PATCH 092/300] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1f137ad18..8a4fd06f5 100644 --- a/README.md +++ b/README.md @@ -52,10 +52,10 @@ Following is a list of funding targets. When a funding target is reached, the fe ### Upgrading *I have an existing RaspAP installation. How do I upgrade to Insiders?* -Upgrading is easy. Simply invoke the Quick Installer with the `--upgrade` switch, specifying the private Insiders repo, like so: +Upgrading is easy. Simply invoke the Quick Installer with the --upgrade switch, specifying the private Insiders Edition, like so: ``` -curl -sL https://install.raspap.com | bash -s -- --upgrade --repo raspap/raspap-insiders +curl -sL https://install.raspap.com | bash -s -- --upgrade --insiders ``` If you haven't [added SSH keys to your GitHub account](https://docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh) you will be prompted to authenticate. If so, just enter your GitHub credentials during the install: From 4a50687e7963bca546b420f91a529d2fceaa1a93 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Mar 2021 10:24:34 +0000 Subject: [PATCH 093/300] Add wg peer default values --- config/defaults.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/config/defaults.json b/config/defaults.json index 55dd0938d..699936735 100644 --- a/config/defaults.json +++ b/config/defaults.json @@ -36,15 +36,17 @@ }, "wireguard": { "server": { - "Address": [ "10.253.3.1/24" ], + "Address": [ "10.8.2.1/24" ], "ListenPort": [ "51820" ], - "DNS": [ "10.3.141.1" ], + "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": { - "Endpoint": [ "10.253.3.1" ], - "AllowedIPs": ["0.0.0.0/0"], + "Address": [ "10.8.1.2/24" ], + "Endpoint": [ "10.8.2.1" ], + "ListenPort": [ "21841" ], + "AllowedIPs": ["10.8.2.0/24"], "PersistentKeepalive": [ "15" ] } } From ddc8c427462ef7c6860fa1f625406f30c22ee7fa Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Mar 2021 10:25:21 +0000 Subject: [PATCH 094/300] Update peer template w/ additional fields --- templates/wg/general.php | 7 +++++++ templates/wg/peers.php | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/templates/wg/general.php b/templates/wg/general.php index db00ab21f..de5555f0b 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -41,6 +41,13 @@ +
+
+ + +
+
+ diff --git a/templates/wg/peers.php b/templates/wg/peers.php index cbdad1282..91e0500ad 100644 --- a/templates/wg/peers.php +++ b/templates/wg/peers.php @@ -26,7 +26,21 @@
-
+
+ + +
+
+ +
+
+ + +
+
+ +
+
From cbab3f2825c1711f3303b34a70e5ff00478f2e3d Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Mar 2021 10:27:08 +0000 Subject: [PATCH 095/300] Update peer input handling, write wg configs --- includes/wireguard.php | 53 ++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/includes/wireguard.php b/includes/wireguard.php index 63b268270..4c4d838d5 100644 --- a/includes/wireguard.php +++ b/includes/wireguard.php @@ -33,11 +33,18 @@ function DisplayWireGuardConfig() $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 = $conf['PublicKey']; + + // 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']; - $wg_peerpubkey = $conf['PublicKey']; - + // fetch service status exec('pidof wg-crypt-wg0 | wc -l', $wgstatus); $serviceStatus = $wgstatus[0] == 0 ? "down" : "up"; @@ -54,6 +61,9 @@ function DisplayWireGuardConfig() "wg_srvpubkey", "wg_srvport", "wg_srvipaddress", + "wg_srvdns", + "wg_pipaddress", + "wg_plistenport", "wg_peerpubkey", "wg_pendpoint", "wg_pallowedips", @@ -76,18 +86,37 @@ function SaveWireGuardConfig($status) // Validate input if (isset($_POST['wg_srvport'])) { if (strlen($_POST['wg_srvport']) > 5 || !is_numeric($_POST['wg_srvport'])) { - $status->addMessage('Invalid value for port number', 'danger'); + $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 IP address', 'danger'); + $status->addMessage('Invalid value for server IP address', 'danger'); + $good_input = false; + } + } + 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_srvdns'])) { + if (!filter_var($_POST['wg_srvdns'],FILTER_VALIDATE_IP)) { + $status->addMessage('Invalid value for DNS', 'danger'); $good_input = false; } } if (isset($_POST['wg_pendpoint']) && strlen(trim($_POST['wg_pendpoint']) >0 )) { - if (!filter_var($_POST['wg_pendpoint'],FILTER_VALIDATE_IP)) { + $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; } @@ -114,15 +143,13 @@ function SaveWireGuardConfig($status) $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']; - if ($_POST['wg_pendpoint'] !== '') { - $config[] = 'Endpoint = '.trim($_POST['wg_pendpoint']).':'.$_POST['wg_srvport']; - } $config[] = 'AllowedIPs = '.$_POST['wg_pallowedips']; if ($_POST['wg_pkeepalive'] !== '') { $config[] = 'PersistentKeepalive = '.trim($_POST['wg_pkeepalive']); @@ -136,15 +163,17 @@ function SaveWireGuardConfig($status) // client1 (client.conf) $config = []; $config[] = '[Interface]'; - if ($_POST['wg_pendpoint'] !== '') { - $config[] = 'Address = '.trim($_POST['wg_pendpoint']); - } + $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_srvipaddress']; + $config[] = 'Endpoint = '.$_POST['wg_pendpoint']; + if ($_POST['wg_pkeepalive'] !== '') { + $config[] = 'PersistentKeepalive = '.trim($_POST['wg_pkeepalive']); + } $config[] = ''; $config = join(PHP_EOL, $config); From 3ac70a3a3cb7d13349aa36d7639012dbf19d3520 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Mar 2021 10:28:14 +0000 Subject: [PATCH 096/300] Move qr_encode() to inc/functions --- app/img/wifi-qr-code.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/img/wifi-qr-code.php b/app/img/wifi-qr-code.php index 21092f82e..e23e1bedf 100644 --- a/app/img/wifi-qr-code.php +++ b/app/img/wifi-qr-code.php @@ -10,11 +10,6 @@ exit; } -function qr_encode($str) -{ - return preg_replace('/(? Date: Sun, 7 Mar 2021 10:39:33 +0000 Subject: [PATCH 097/300] Add PHP_EOLs to parsed client.conf --- app/img/wg-qr-code.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/img/wg-qr-code.php b/app/img/wg-qr-code.php index 83cda725d..7a66e6fc2 100644 --- a/app/img/wg-qr-code.php +++ b/app/img/wg-qr-code.php @@ -11,7 +11,8 @@ } exec("sudo cat " .RASPI_WIREGUARD_PATH.'client.conf', $return); -$peer_conf = qr_encode(implode($return)); +$peer_conf = implode(PHP_EOL,$return); +$peer_conf.= PHP_EOL; $command = "qrencode -t svg -m 0 -o - " . mb_escapeshellarg($peer_conf); $svg = shell_exec($command); $etag = hash('sha256', $peer_conf); From 96bd34f07fe5dcc3b4107f4dd7834bc23591f160 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Mar 2021 13:19:56 +0000 Subject: [PATCH 098/300] Add event listener for Bootstrap form validation --- app/js/custom.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/js/custom.js b/app/js/custom.js index 25260e812..7ad6b1667 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -361,6 +361,22 @@ $('.wg-keygen').click(function(){ }) }) +// Event listener for Bootstrap's form validation +window.addEventListener('load', function() { + // Fetch all the forms we want to apply custom Bootstrap validation styles to + var forms = document.getElementsByClassName('needs-validation'); + // Loop over them and prevent submission + var validation = Array.prototype.filter.call(forms, function(form) { + form.addEventListener('submit', function(event) { + if (form.checkValidity() === false) { + event.preventDefault(); + event.stopPropagation(); + } + form.classList.add('was-validated'); + }, false); + }); +}, false); + // Static Array method Array.range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start); From 0b3307ce1f3bbd0d5bc06daa757de3014206257c Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Mar 2021 13:20:52 +0000 Subject: [PATCH 099/300] Add required fields for validation --- templates/wg/general.php | 9 +++++---- templates/wg/peers.php | 12 ++++++------ templates/wireguard.php | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/templates/wg/general.php b/templates/wg/general.php index de5555f0b..51eb53095 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -19,7 +19,7 @@
- +
@@ -30,21 +30,21 @@
- +
- +
- +
@@ -52,3 +52,4 @@
+ diff --git a/templates/wg/peers.php b/templates/wg/peers.php index 91e0500ad..c259e5413 100644 --- a/templates/wg/peers.php +++ b/templates/wg/peers.php @@ -17,7 +17,7 @@
- +
@@ -28,35 +28,35 @@
- +
- +
- +
- +
- +
diff --git a/templates/wireguard.php b/templates/wireguard.php index ee28dfa17..455546552 100644 --- a/templates/wireguard.php +++ b/templates/wireguard.php @@ -27,7 +27,7 @@
showMessages(); ?> - +
diff --git a/templates/networking.php b/templates/networking.php index 0ab9152ed..90a3ab007 100755 --- a/templates/networking.php +++ b/templates/networking.php @@ -1,23 +1,27 @@
-
-
-
-
- -
-
-
+
+
+
+
+ +
+
+

-
+
@@ -74,9 +78,122 @@
+
+
+ +
+
+
+

+
+
+ +
+ + " > +
+

+
+ + " > + + " > + + " > +
+ +
+
+
+
+

+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + 0) { + foreach($clients["device"] as $id => $dev) { + echo ""; + echo "\n"; + echo "\n"; + $ty="Client"; + if(isset($dev["isAP"]) && $dev["isAP"]) $ty="Access Point"; + echo "\n"; + echo "\n"; + echo "\n"; + $udevfile=$_SESSION["udevrules"]["udev_rules_file"]; + $isStatic=array(); + exec('find /etc/udev/rules.d/ -type f \( -iname "*.rules" ! -iname "'.basename($udevfile).'" \) -exec grep -i '.$dev["mac"].' {} \; ',$isStatic); + if(empty($isStatic)) + exec('find /etc/udev/rules.d/ -type f \( -iname "*.rules" ! -iname "'.basename($udevfile).'" \) -exec grep -i '.$dev["vid"].' {} \; | grep -i '.$dev["pid"].' ',$isStatic); + $isStatic = empty($isStatic) ? false : true; + $devname=array(); + exec('grep -i '.$dev["vid"].' '.$udevfile.' | grep -i '.$dev["pid"].' | sed -rn \'s/.*name=\"(\w*)\".*/\1/ip\' ',$devname); + if(!empty($devname)) $devname=$devname[0]; + else { + exec('grep -i '.$dev["mac"].' '.$udevfile.' | sed -rn \'s/.*name=\"(\w*)\".*/\1/ip\' ',$devname); + if(!empty($devname)) $devname=$devname[0]; + } + if(empty($devname)) $devname=""; + $isStatic = $isStatic || $dev["type"] === "ppp"; + $txtdisabled=$isStatic ? "disabled":""; + echo '"; + echo ''."\n"; + echo '\n"; + echo "\n"; + } + } + } else echo ""; + ?> + +
".$dev["vendor"]." ".$dev["model"]."".$dev["name"]."".$ty."".$dev["mac"]."".$dev["vid"]."/".$dev["pid"]."'; + if (! $isStatic ) echo ''."\n"; + else echo $dev["name"]; + echo ''."\n"; + echo ''."\n"; + echo ''."\n"; + echo ''."\n"; + echo ''; + if (! $isStatic) echo 'Change'; + echo "
No network devices found
+
+
+
+
+
-
+
From d920f739435b40165e2219151b169fda4d5fc6ad Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Sun, 7 Mar 2021 18:48:53 +0100 Subject: [PATCH 102/300] Add client handling to installer --- installers/common.sh | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/installers/common.sh b/installers/common.sh index 6acb021fe..67f4dea09 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -26,6 +26,7 @@ readonly raspap_adblock="/etc/dnsmasq.d/090_adblock.conf" readonly raspap_sysctl="/etc/sysctl.d/90_raspap.conf" readonly raspap_network="$raspap_dir/networking/" readonly rulesv4="/etc/iptables/rules.v4" +readonly raspap_client_scripts="/usr/local/sbin" 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 @@ -50,6 +51,7 @@ function _install_raspap() { _configure_networking _prompt_install_adblock _prompt_install_openvpn + _install_client_config _patch_system_files _install_complete } @@ -145,6 +147,7 @@ function _install_dependencies() { echo iptables-persistent iptables-persistent/autosave_v4 boolean true | sudo debconf-set-selections echo iptables-persistent iptables-persistent/autosave_v6 boolean true | sudo debconf-set-selections sudo apt-get install $apt_option lighttpd git hostapd dnsmasq iptables-persistent $php_package $dhcpcd_package vnstat qrencode || _install_status 1 "Unable to install dependencies" + sudo apt-get install wvdial socat bc || _install_status 1 "Unable to install dependencies" _install_status 0 } @@ -170,6 +173,11 @@ function _create_raspap_directories() { # Create a directory to store networking configs echo "Creating $raspap_dir/networking" sudo mkdir -p "$raspap_dir/networking" + # Copy existing dhcpcd.conf to use as base config + echo "Adding /etc/dhcpcd.conf as base configuration" + cat /etc/dhcpcd.conf | sudo tee -a /etc/raspap/networking/defaults > /dev/null + echo "Changing file ownership of $raspap_dir" + sudo chown -R $raspap_user:$raspap_user "$raspap_dir" || _install_status 1 "Unable to change file ownership for '$raspap_dir'" } # Generate hostapd logging and service control scripts @@ -464,6 +472,23 @@ function _enable_raspap_daemon() { sudo systemctl enable raspapd.service || _install_status 1 "Failed to enable raspap.service" } +function _install_client_config() { + _install_log "Install mobile client scripts and settings" + # Move scripts + sudo cp "$webroot_dir/config/client_config/"*.sh "$raspap_client_scripts/" || _install_status 1 "Unable to move client scripts" + sudo chmod a+rx "$raspap_client_scripts/"*.sh || _install_status 1 "Unable to chmod client scripts" + sudo cp "$webroot_dir/config/client_config/mcc-mnc-table.csv" "$raspap_client_scripts/" || _install_status 1 "Unable to move client data" + # wvdial settings + sudo cp "$webroot_dir/config/client_config/wvdial.conf" "/etc/" || _install_status 1 "Unable to install client configuration" + sudo cp "$webroot_dir/config/client_config/interfaces" "/etc/network/interfaces" || _install_status 1 "Unable to install interface settings" + # udev rules/services to auto start mobile data services + sudo cp "$webroot_dir/config/client_config/70-mobile-data-sticks.rules" "/etc/udev/rules.d/" || _install_status 1 "Unable to install client udev rules" + sudo cp "$webroot_dir/config/client_config/80-raspap-net-devices.rules" "/etc/udev/rules.d/" || _install_status 1 "Unable to install client udev rules" + sudo cp "$webroot_dir/config/client_config/"*.service "/etc/systemd/system/" || _install_status 1 "Unable to install client startup services" + # client configuration and udev rule templates + sudo cp "$webroot_dir/config/client_udev_prototypes.json" "/etc/raspap/networking/" || _install_status 1 "Unable to install client configuration" +} + # Configure IP forwarding, set IP tables rules, prompt to install RaspAP daemon function _configure_networking() { _install_log "Configuring networking" From 1e7438da23e079bda2172a5c88066783fcd5276b Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 7 Mar 2021 19:06:53 +0000 Subject: [PATCH 103/300] Code cleanup, update Endpoint default value --- app/js/custom.js | 1 + config/defaults.json | 2 +- includes/wireguard.php | 5 ++++- templates/wg/general.php | 8 ++++---- templates/wg/peers.php | 12 +++++++----- templates/wireguard.php | 2 +- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/app/js/custom.js b/app/js/custom.js index 7ad6b1667..b2214c9c1 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -368,6 +368,7 @@ window.addEventListener('load', function() { // Loop over them and prevent submission var validation = Array.prototype.filter.call(forms, function(form) { form.addEventListener('submit', function(event) { + //console.log(event.submitter); if (form.checkValidity() === false) { event.preventDefault(); event.stopPropagation(); diff --git a/config/defaults.json b/config/defaults.json index 699936735..faab15b6a 100644 --- a/config/defaults.json +++ b/config/defaults.json @@ -44,7 +44,7 @@ }, "peer": { "Address": [ "10.8.1.2/24" ], - "Endpoint": [ "10.8.2.1" ], + "Endpoint": [ "10.8.2.1:51820" ], "ListenPort": [ "21841" ], "AllowedIPs": ["10.8.2.0/24"], "PersistentKeepalive": [ "15" ] diff --git a/includes/wireguard.php b/includes/wireguard.php index 86665a483..b0d0aacd4 100644 --- a/includes/wireguard.php +++ b/includes/wireguard.php @@ -44,6 +44,9 @@ function DisplayWireGuardConfig() $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); @@ -56,12 +59,12 @@ function DisplayWireGuardConfig() "wg_state", "serviceStatus", "wg_log", - "endpoint_enable", "peer_id", "wg_srvpubkey", "wg_srvport", "wg_srvipaddress", "wg_srvdns", + "wg_penabled", "wg_pipaddress", "wg_plistenport", "wg_peerpubkey", diff --git a/templates/wg/general.php b/templates/wg/general.php index 51eb53095..b3235e88c 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -19,7 +19,7 @@
- +
@@ -30,21 +30,21 @@
- +
- +
- +
diff --git a/templates/wg/peers.php b/templates/wg/peers.php index c259e5413..a4d80f5af 100644 --- a/templates/wg/peers.php +++ b/templates/wg/peers.php @@ -17,7 +17,7 @@
- +
@@ -28,28 +28,28 @@
- +
- +
- +
- +
@@ -63,8 +63,10 @@
+ RaspAP Wifi QR code
+
diff --git a/templates/wireguard.php b/templates/wireguard.php index 455546552..ee28dfa17 100644 --- a/templates/wireguard.php +++ b/templates/wireguard.php @@ -27,7 +27,7 @@
showMessages(); ?> -
+
From 03acf8f92c481d9e545769231525d267e0183409 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 8 Mar 2021 08:59:38 +0000 Subject: [PATCH 107/300] Minor: update timestamp --- locale/en_US/LC_MESSAGES/messages.mo | Bin 21077 -> 21077 bytes locale/en_US/LC_MESSAGES/messages.po | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/locale/en_US/LC_MESSAGES/messages.mo b/locale/en_US/LC_MESSAGES/messages.mo index 23c18e522906eee2d279c4e0bd2565f0ffd271c1..fd81791e6c75dcabb201ea6f86cf88f95198c69e 100644 GIT binary patch delta 28 kcmcb*gz@SU#tmtvyoS04#<~U;3I>)|1_qmpO&`es0FLMh9{>OV delta 28 kcmcb*gz@SU#tmtvyau`k#=1t93I+yN2Bw>fO&`es0FKEC9RL6T diff --git a/locale/en_US/LC_MESSAGES/messages.po b/locale/en_US/LC_MESSAGES/messages.po index c10c8da28..3170fa4ec 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" From dd7873fd41531e3fc4848ce027c95f878464b637 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Mon, 8 Mar 2021 14:35:17 +0100 Subject: [PATCH 108/300] Allow installation from a private repository via access token --- installers/raspbian.sh | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/installers/raspbian.sh b/installers/raspbian.sh index 469fff7fd..230f4887e 100755 --- a/installers/raspbian.sh +++ b/installers/raspbian.sh @@ -17,6 +17,7 @@ # -a, --adblock Used with -y, --yes, sets Adblock install option (0=no install) # -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 # -u, --upgrade Upgrades an existing installation to the latest release version # -i, --insiders Installs from the Insiders Edition (raspap/raspap-insiders) # -v, --version Outputs release info and exits @@ -37,7 +38,6 @@ set -eo pipefail function _main() { # set defaults repo="raspap/raspap-webgui" # override with -r, --repo option - _parse_params "$@" _setup_colors _log_output @@ -50,6 +50,7 @@ function _parse_params() { upgrade=0 ovpn_option=1 adblock_option=1 + acctoken="" while :; do case "${1-}" in @@ -85,6 +86,9 @@ function _parse_params() { -i|--insiders) repo="raspap/raspap-insiders" ;; + -t|--token) + acctoken="$2" + ;; -v|--version) _version ;; @@ -176,7 +180,9 @@ function _display_welcome() { function _get_release() { if [ "$repo" == "raspap/raspap-insiders" ]; then readonly RASPAP_LATEST="Insiders" - branch="master" + if [ -z ${branch} ]; then + branch="master" + fi else readonly RASPAP_LATEST=$(curl -s "https://api.github.com/repos/$repo/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")' ) fi @@ -214,6 +220,7 @@ function _update_system_packages() { # Fetch required installer functions function _load_installer() { + # fetch latest release tag _get_release @@ -223,14 +230,18 @@ function _load_installer() { fi UPDATE_URL="https://raw.githubusercontent.com/$repo/$branch/" + header=() + if [[ ! -z "$acctoken" ]]; then + header=(--header "Authorization: token $acctoken") + fi if [ "${install_cert:-}" = 1 ]; then source="mkcert" - wget -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_certificate || _install_status 1 "Unable to install certificate" else source="common" - wget -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 From e8d0fab4638248c41fcae50f1d3a6a96637c6adc Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Mon, 8 Mar 2021 16:44:20 +0100 Subject: [PATCH 109/300] Fix the sidebar logo path --- index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.php b/index.php index 7e157bfe6..806d7241e 100755 --- a/index.php +++ b/index.php @@ -115,7 +115,7 @@
Status
From 445b0af4b56d7cda80efa5f1bc5f8cf38c149824 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 9 Mar 2021 09:51:15 +0000 Subject: [PATCH 110/300] Add @zbchristian's token option, fix private repo handling --- installers/common.sh | 78 ++++++++++++++++++++++++++++++++++++++++-- installers/raspbian.sh | 34 ++++++++++++------ 2 files changed, 99 insertions(+), 13 deletions(-) diff --git a/installers/common.sh b/installers/common.sh index 6acb021fe..d38c0b4f2 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -26,9 +26,15 @@ readonly raspap_adblock="/etc/dnsmasq.d/090_adblock.conf" readonly raspap_sysctl="/etc/sysctl.d/90_raspap.conf" readonly raspap_network="$raspap_dir/networking/" readonly rulesv4="/etc/iptables/rules.v4" +readonly raspap_client_scripts="/usr/local/sbin" 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() { @@ -50,6 +56,8 @@ function _install_raspap() { _configure_networking _prompt_install_adblock _prompt_install_openvpn + _prompt_install_wireguard + _install_client_config _patch_system_files _install_complete } @@ -77,7 +85,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]: " @@ -145,6 +153,7 @@ function _install_dependencies() { echo iptables-persistent iptables-persistent/autosave_v4 boolean true | sudo debconf-set-selections echo iptables-persistent iptables-persistent/autosave_v6 boolean true | sudo debconf-set-selections sudo apt-get install $apt_option lighttpd git hostapd dnsmasq iptables-persistent $php_package $dhcpcd_package vnstat qrencode || _install_status 1 "Unable to install dependencies" + sudo apt-get install wvdial socat bc || _install_status 1 "Unable to install dependencies" _install_status 0 } @@ -170,6 +179,11 @@ function _create_raspap_directories() { # Create a directory to store networking configs echo "Creating $raspap_dir/networking" sudo mkdir -p "$raspap_dir/networking" + # Copy existing dhcpcd.conf to use as base config + echo "Adding /etc/dhcpcd.conf as base configuration" + cat /etc/dhcpcd.conf | sudo tee -a /etc/raspap/networking/defaults > /dev/null + echo "Changing file ownership of $raspap_dir" + sudo chown -R $raspap_user:$raspap_user "$raspap_dir" || _install_status 1 "Unable to change file ownership for '$raspap_dir'" } # Generate hostapd logging and service control scripts @@ -313,6 +327,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" @@ -464,6 +521,23 @@ function _enable_raspap_daemon() { sudo systemctl enable raspapd.service || _install_status 1 "Failed to enable raspap.service" } +function _install_client_config() { + _install_log "Install mobile client scripts and settings" + # Move scripts + sudo cp "$webroot_dir/config/client_config/"*.sh "$raspap_client_scripts/" || _install_status 1 "Unable to move client scripts" + sudo chmod a+rx "$raspap_client_scripts/"*.sh || _install_status 1 "Unable to chmod client scripts" + sudo cp "$webroot_dir/config/client_config/mcc-mnc-table.csv" "$raspap_client_scripts/" || _install_status 1 "Unable to move client data" + # wvdial settings + sudo cp "$webroot_dir/config/client_config/wvdial.conf" "/etc/" || _install_status 1 "Unable to install client configuration" + sudo cp "$webroot_dir/config/client_config/interfaces" "/etc/network/interfaces" || _install_status 1 "Unable to install interface settings" + # udev rules/services to auto start mobile data services + sudo cp "$webroot_dir/config/client_config/70-mobile-data-sticks.rules" "/etc/udev/rules.d/" || _install_status 1 "Unable to install client udev rules" + sudo cp "$webroot_dir/config/client_config/80-raspap-net-devices.rules" "/etc/udev/rules.d/" || _install_status 1 "Unable to install client udev rules" + sudo cp "$webroot_dir/config/client_config/"*.service "/etc/systemd/system/" || _install_status 1 "Unable to install client startup services" + # client configuration and udev rule templates + sudo cp "$webroot_dir/config/client_udev_prototypes.json" "/etc/raspap/networking/" || _install_status 1 "Unable to install client configuration" +} + # Configure IP forwarding, set IP tables rules, prompt to install RaspAP daemon function _configure_networking() { _install_log "Configuring networking" diff --git a/installers/raspbian.sh b/installers/raspbian.sh index 469fff7fd..da84a7288 100755 --- a/installers/raspbian.sh +++ b/installers/raspbian.sh @@ -17,6 +17,7 @@ # -a, --adblock Used with -y, --yes, sets Adblock install option (0=no install) # -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 # -u, --upgrade Upgrades an existing installation to the latest release version # -i, --insiders Installs from the Insiders Edition (raspap/raspap-insiders) # -v, --version Outputs release info and exits @@ -36,8 +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 @@ -50,6 +50,8 @@ function _parse_params() { upgrade=0 ovpn_option=1 adblock_option=1 + insiders=0 + acctoken="" while :; do case "${1-}" in @@ -83,7 +85,10 @@ function _parse_params() { upgrade=1 ;; -i|--insiders) - repo="raspap/raspap-insiders" + insiders=1 + ;; + -t|--token) + acctoken="$2" ;; -v|--version) _version @@ -129,6 +134,7 @@ OPTIONS: -a, --adblock Used with -y, --yes, sets Adblock install option (0=no install) -r, --repo, --repository Overrides the default GitHub repo (raspap/raspap-webgui) -b, --branch Overrides the default git branch (latest release) +-t, --token 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) -v, --version Outputs release info and exits @@ -153,7 +159,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 } @@ -167,18 +173,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 function _get_release() { - if [ "$repo" == "raspap/raspap-insiders" ]; then - readonly RASPAP_LATEST="Insiders" - branch="master" + readonly RASPAP_LATEST=$(curl -s "https://api.github.com/repos/$repo/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")' ) + if [ "$insiders" == 1 ]; then + RASPAP_INSIDERS_LATEST=$(curl -s "https://install.raspap.com/repos/RaspAP/raspap-insiders/releases/latest/" | grep -Po '"tag_name": "\K.*?(?=")' ) + 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.*?(?=")' ) + RASPAP_RELEASE="${RASPAP_LATEST}" fi } @@ -214,6 +221,7 @@ function _update_system_packages() { # Fetch required installer functions function _load_installer() { + # fetch latest release tag _get_release @@ -223,14 +231,18 @@ function _load_installer() { fi UPDATE_URL="https://raw.githubusercontent.com/$repo/$branch/" + header=() + if [[ ! -z "$acctoken" ]]; then + header=(--header "Authorization: token $acctoken") + fi if [ "${install_cert:-}" = 1 ]; then source="mkcert" - wget -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_certificate || _install_status 1 "Unable to install certificate" else source="common" - wget -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 From 76e87508bdc23cee5d10580cb2b0c00cb440269f Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 9 Mar 2021 09:58:05 +0000 Subject: [PATCH 111/300] Remove feature branch routines --- installers/common.sh | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/installers/common.sh b/installers/common.sh index d38c0b4f2..8b532be36 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -26,7 +26,6 @@ readonly raspap_adblock="/etc/dnsmasq.d/090_adblock.conf" readonly raspap_sysctl="/etc/sysctl.d/90_raspap.conf" readonly raspap_network="$raspap_dir/networking/" readonly rulesv4="/etc/iptables/rules.v4" -readonly raspap_client_scripts="/usr/local/sbin" readonly notracking_url="https://raw.githubusercontent.com/notracking/hosts-blocklists/master/" webroot_dir="/var/www/html" @@ -57,7 +56,6 @@ function _install_raspap() { _prompt_install_adblock _prompt_install_openvpn _prompt_install_wireguard - _install_client_config _patch_system_files _install_complete } @@ -153,7 +151,6 @@ function _install_dependencies() { echo iptables-persistent iptables-persistent/autosave_v4 boolean true | sudo debconf-set-selections echo iptables-persistent iptables-persistent/autosave_v6 boolean true | sudo debconf-set-selections sudo apt-get install $apt_option lighttpd git hostapd dnsmasq iptables-persistent $php_package $dhcpcd_package vnstat qrencode || _install_status 1 "Unable to install dependencies" - sudo apt-get install wvdial socat bc || _install_status 1 "Unable to install dependencies" _install_status 0 } @@ -179,9 +176,6 @@ function _create_raspap_directories() { # Create a directory to store networking configs echo "Creating $raspap_dir/networking" sudo mkdir -p "$raspap_dir/networking" - # Copy existing dhcpcd.conf to use as base config - echo "Adding /etc/dhcpcd.conf as base configuration" - cat /etc/dhcpcd.conf | sudo tee -a /etc/raspap/networking/defaults > /dev/null echo "Changing file ownership of $raspap_dir" sudo chown -R $raspap_user:$raspap_user "$raspap_dir" || _install_status 1 "Unable to change file ownership for '$raspap_dir'" } @@ -521,23 +515,6 @@ function _enable_raspap_daemon() { sudo systemctl enable raspapd.service || _install_status 1 "Failed to enable raspap.service" } -function _install_client_config() { - _install_log "Install mobile client scripts and settings" - # Move scripts - sudo cp "$webroot_dir/config/client_config/"*.sh "$raspap_client_scripts/" || _install_status 1 "Unable to move client scripts" - sudo chmod a+rx "$raspap_client_scripts/"*.sh || _install_status 1 "Unable to chmod client scripts" - sudo cp "$webroot_dir/config/client_config/mcc-mnc-table.csv" "$raspap_client_scripts/" || _install_status 1 "Unable to move client data" - # wvdial settings - sudo cp "$webroot_dir/config/client_config/wvdial.conf" "/etc/" || _install_status 1 "Unable to install client configuration" - sudo cp "$webroot_dir/config/client_config/interfaces" "/etc/network/interfaces" || _install_status 1 "Unable to install interface settings" - # udev rules/services to auto start mobile data services - sudo cp "$webroot_dir/config/client_config/70-mobile-data-sticks.rules" "/etc/udev/rules.d/" || _install_status 1 "Unable to install client udev rules" - sudo cp "$webroot_dir/config/client_config/80-raspap-net-devices.rules" "/etc/udev/rules.d/" || _install_status 1 "Unable to install client udev rules" - sudo cp "$webroot_dir/config/client_config/"*.service "/etc/systemd/system/" || _install_status 1 "Unable to install client startup services" - # client configuration and udev rule templates - sudo cp "$webroot_dir/config/client_udev_prototypes.json" "/etc/raspap/networking/" || _install_status 1 "Unable to install client configuration" -} - # Configure IP forwarding, set IP tables rules, prompt to install RaspAP daemon function _configure_networking() { _install_log "Configuring networking" From 80c525c042a1e97f8eee00a754494010fd588ef7 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 9 Mar 2021 10:58:15 +0000 Subject: [PATCH 112/300] Minor: update comments --- installers/raspbian.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installers/raspbian.sh b/installers/raspbian.sh index da84a7288..fed9b6196 100755 --- a/installers/raspbian.sh +++ b/installers/raspbian.sh @@ -178,7 +178,7 @@ function _display_welcome() { 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() { readonly RASPAP_LATEST=$(curl -s "https://api.github.com/repos/$repo/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")' ) if [ "$insiders" == 1 ]; then From cb58e310895e45b26e592521a52050ac81632382 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 9 Mar 2021 15:35:48 +0000 Subject: [PATCH 113/300] Handle server/peer enable states --- includes/wireguard.php | 184 +++++++++++++++++++++----------------- installers/raspap.sudoers | 2 + templates/wg/general.php | 6 +- templates/wg/peers.php | 9 +- 4 files changed, 114 insertions(+), 87 deletions(-) diff --git a/includes/wireguard.php b/includes/wireguard.php index 00b4e9623..c7f56cdd6 100644 --- a/includes/wireguard.php +++ b/includes/wireguard.php @@ -34,7 +34,10 @@ function DisplayWireGuardConfig() $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 = $conf['PublicKey']; + $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); @@ -64,6 +67,7 @@ function DisplayWireGuardConfig() "wg_srvport", "wg_srvipaddress", "wg_srvdns", + "wg_senabled", "wg_penabled", "wg_pipaddress", "wg_plistenport", @@ -86,102 +90,120 @@ function SaveWireGuardConfig($status) // Set defaults $good_input = true; $peer_id = 1; - // Validate input - 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; + // 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_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_srvipaddress'])) { + if (!validateCidr($_POST['wg_srvipaddress'])) { + $status->addMessage('Invalid value for server IP address', 'danger'); + $good_input = false; + } } - } - 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_srvdns'])) { + if (!filter_var($_POST['wg_srvdns'],FILTER_VALIDATE_IP)) { + $status->addMessage('Invalid value for DNS', '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_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_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; + 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) { - // fetch private keys from filesytem - $wg_srvprivkey = exec('sudo cat '. RASPI_WIREGUARD_PATH .'wg-server-private.key', $return); - $wg_peerprivkey = exec('sudo cat '. RASPI_WIREGUARD_PATH .'wg-peer-private.key', $return); - // server (wg0.conf) - $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); + 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); - // client1 (client.conf) - $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']); + 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); } - $config[] = ''; - $config = join(PHP_EOL, $config); + // 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); + 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") { diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index 9aded5a3e..18e4ddf8a 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -50,4 +50,6 @@ 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 diff --git a/templates/wg/general.php b/templates/wg/general.php index b3235e88c..06d098112 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -4,13 +4,13 @@

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

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

diff --git a/templates/wg/peers.php b/templates/wg/peers.php index 0d29b429a..319cc0f99 100644 --- a/templates/wg/peers.php +++ b/templates/wg/peers.php @@ -4,12 +4,15 @@

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

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

From 63267cd225fbb9863082a964454a70ab7a455513 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 9 Mar 2021 15:41:03 +0000 Subject: [PATCH 114/300] Update en_US locale --- locale/en_US/LC_MESSAGES/messages.po | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/locale/en_US/LC_MESSAGES/messages.po b/locale/en_US/LC_MESSAGES/messages.po index 3170fa4ec..dcd00a5cc 100644 --- a/locale/en_US/LC_MESSAGES/messages.po +++ b/locale/en_US/LC_MESSAGES/messages.po @@ -855,12 +855,15 @@ msgstr "Invalid custom host found on line " msgid "Tunnel settings" msgstr "Tunnel settings" -msgid "Enable tunnel" -msgstr "Enable tunnel" +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" @@ -876,8 +879,14 @@ msgstr "DNS" msgid "Peer" msgstr "Peer" -msgid "Enable endpoint" -msgstr "Enable endpoint" +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" From 9a770329db894f53f6aa8b2e87cc890521f19c67 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 9 Mar 2021 17:54:30 +0000 Subject: [PATCH 115/300] Update release version --- README.md | 2 +- includes/defaults.php | 2 +- index.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f754d941b..3cdbbc689 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/) +[![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. diff --git a/includes/defaults.php b/includes/defaults.php index f6009f174..34e083780 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -6,7 +6,7 @@ $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', diff --git a/index.php b/index.php index 59549062e..60c6088bf 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/ From 76e2eecaec5817f07ee3f890988f29a0717afa43 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Wed, 10 Mar 2021 23:45:04 +0100 Subject: [PATCH 116/300] Correct internet device GUI and udev config --- ajax/networking/save_net_dev_config.php | 2 +- app/js/custom.js | 4 +- config/config.php | 2 + includes/dashboard.php | 9 +++-- includes/functions.php | 5 ++- includes/get_clients.php | 4 ++ includes/wifi_functions.php | 1 + templates/dashboard.php | 2 +- templates/networking.php | 52 ++++++++++++------------- 9 files changed, 46 insertions(+), 35 deletions(-) diff --git a/ajax/networking/save_net_dev_config.php b/ajax/networking/save_net_dev_config.php index 6c34e0533..af79cfb4d 100644 --- a/ajax/networking/save_net_dev_config.php +++ b/ajax/networking/save_net_dev_config.php @@ -88,7 +88,7 @@ $cfg['static'] = $_POST[$int.'-static']; $cfg['failover'] = $_POST[$int.'-failover']; } - if (write_php_ini($cfg, RASPI_CONFIG_NETWORKING.'/'.$file)) { + if (write_php_ini($cfg, RASPI_CONFIG.'/networking/'.$file)) { $jsonData = ['return'=>0,'output'=>['Successfully Updated Network Configuration']]; } else { $jsonData = ['return'=>1,'output'=>['Error saving network configuration to file']]; diff --git a/app/js/custom.js b/app/js/custom.js index f298a77e8..fff618af8 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -130,8 +130,8 @@ function setupBtns() { $('#btnSummaryRefresh').click(function(){getAllInterfaces();}); $('.intsave').click(function(){ var int = $(this).data('int'); - saveNetDeviceSettings(int); - saveNetworkSettings(int); + var opts = $(this).data('opts'); + saveNetDeviceSettings(int,opts); }); $('.intapply').click(function(){ applyNetworkSettings(); diff --git a/config/config.php b/config/config.php index 2c476b570..f0516cfc1 100755 --- a/config/config.php +++ b/config/config.php @@ -25,6 +25,8 @@ define('RASPI_LIGHTTPD_CONFIG', '/etc/lighttpd/lighttpd.conf'); define('RASPI_ACCESS_CHECK_IP', '1.1.1.1'); define('RASPI_ACCESS_CHECK_DNS', 'one.one.one.one'); +define('RASPI_CLIENT_CONFIG_PATH', '/etc/raspap/networking/client_udev_prototypes.json'); +define('RASPI_CLIENT_SCRIPT_PATH', '/usr/local/sbin'); // Constant for the 5GHz wireless regulatory domain define('RASPI_5GHZ_ISO_ALPHA2', array('NL','US')); diff --git a/includes/dashboard.php b/includes/dashboard.php index 6d74581cf..93e640c0e 100755 --- a/includes/dashboard.php +++ b/includes/dashboard.php @@ -96,12 +96,12 @@ function DisplayDashboard(&$extraFooterScripts) $clientinfo=array("name"=>"none","type"=>-1,"connected"=>"n"); $raspi_client=$_SESSION['wifi_client_interface']; load_client_config(); - $clients = getClients(false); - if(!empty($clients)) { - $ncl=$clients["clients"]; + $client_devs = getClients(false); + if(!empty($client_devs)) { + $ncl=$client_devs["clients"]; if($ncl > 0) { $ty=-1; - foreach($clients["device"] as $dev) { + foreach($client_devs["device"] as $dev) { if(($id=array_search($dev["type"],$_SESSION["net-device-types"])) > $ty && !$dev["isAP"]) { $ty=$id; $clientinfo=$dev; @@ -162,6 +162,7 @@ function DisplayDashboard(&$extraFooterScripts) $client_interface = $clientinfo["name"]; } $apInterface = $_SESSION['ap_interface']; + $clientInterface = $raspi_client; $MACPattern = '"([[:xdigit:]]{2}:){5}[[:xdigit:]]{2}"'; if (getBridgedState()) { $moreLink = "hostapd_conf"; diff --git a/includes/functions.php b/includes/functions.php index 106991902..2a70712bf 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -728,6 +728,8 @@ function getNightmode(){ } else { return false; } +} + // search array for matching string and return only first matching group function preg_only_match($pat,$haystack) { $match = ""; @@ -738,5 +740,6 @@ function preg_only_match($pat,$haystack) { } return $match; } -} + +?> diff --git a/includes/get_clients.php b/includes/get_clients.php index 999b5bfad..b04610a47 100644 --- a/includes/get_clients.php +++ b/includes/get_clients.php @@ -137,6 +137,10 @@ function getClients($simple=true) { break; default: } + if (!isset($cl["device"][$i]["signal"])){ + $cl["device"][$i]["signal"]= $cl["device"][$i]["connected"] == "n" ? "-100 dB (0%)": "0 dB (100%)";; + } + if (!isset($cl["device"][$i]["isAP"])) $cl["device"][$i]["isAP"]=false; } } return $cl; diff --git a/includes/wifi_functions.php b/includes/wifi_functions.php index 8296c57c9..7aa4cecef 100755 --- a/includes/wifi_functions.php +++ b/includes/wifi_functions.php @@ -157,3 +157,4 @@ function getWifiInterface() } } +?> \ No newline at end of file diff --git a/templates/dashboard.php b/templates/dashboard.php index 9c185ac62..bf841b599 100755 --- a/templates/dashboard.php +++ b/templates/dashboard.php @@ -107,7 +107,7 @@ - + diff --git a/templates/networking.php b/templates/networking.php index 90a3ab007..6df5ba874 100755 --- a/templates/networking.php +++ b/templates/networking.php @@ -13,8 +13,8 @@
@@ -84,31 +84,6 @@ -
-
-
-

-
- - -
- - " > -
-

-
- - " > - - " > - - " > -
- - -
-
-

@@ -194,6 +169,31 @@
+
+
+
+

+
+
+ +
+ + " > +
+

+
+ + " > + + " > + + " > +
+ +
+
+
+
From 2b4c9472e6e4dc01605756d5f8bee6cb62cefb27 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Fri, 12 Mar 2021 12:20:34 +0100 Subject: [PATCH 117/300] Replace switchClientState.sh by php function in get_clients.php Cleanup display of client on dashboard --- config/client_config/switchClientState.sh | 49 -------------------- includes/dashboard.php | 38 ++++++---------- includes/get_clients.php | 54 ++++++++++++++++++++++- installers/raspap.sudoers | 5 ++- templates/dashboard.php | 41 ++++++++--------- 5 files changed, 90 insertions(+), 97 deletions(-) delete mode 100644 config/client_config/switchClientState.sh diff --git a/config/client_config/switchClientState.sh b/config/client_config/switchClientState.sh deleted file mode 100644 index e6e4635f7..000000000 --- a/config/client_config/switchClientState.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash -# -# parameters: up or down -# current client from getClients.sh -# -# requires: getClients.sh, onoff_huawei_hilink.sh in same path -# ifconfig, ifup, ifdown, systemd-cat -# -# -# zbchristian 2020 - -path=`dirname $0` -clients=$($path/getClients.sh simple) - -if [[ -z $clients ]] || [[ $(echo $clients| grep -oP '(?<="clients": )\d') == 0 ]]; then echo "$0 : No client found"|systemd-cat; exit; fi - -devs=( $(echo $clients | grep -oP '(?<="name": ")\w*(?=")') ) -types=( $(echo $clients | grep -oP '(?<="type": )\d') ) - -# find the device with the max type number -imax=0 -type=0 -for i in "${!devs[@]}"; do - if [[ ${types[$i]} > $type ]]; then imax=$i; type=${types[$i]}; fi -done -device=${devs[$imax]} - -echo "$0: try to set $device $1" | systemd-cat - -connected=`ifconfig -a | grep -i $device -A 1 | grep -oP "(?<=inet )([0-9]{1,3}\.){3}[0-9]{1,3}"` - -if [ -z "$connected" ] && [[ $1 == "up" ]]; then - if [[ $type == 3 ]]; then ip link set $device up; fi - if [[ $type == 5 ]]; then ifup $device; fi -fi -if [[ ! -z "$connected" ]] && [[ $1 == "down" ]]; then - if [[ $type == 3 ]]; then ip link set $device down; fi - if [[ $type == 5 ]]; then ifdown $device; fi -fi -if [[ $type == 4 ]]; then - ipadd=$(echo $connected | grep -oP "([0-9]{1,3}\.){2}[0-9]{1,3}")".1" # get ip address of the Hilink API - mode=0 - if [[ $1 == "up" ]]; then mode=1; fi - if [[ -f /etc/raspap/networking/mobiledata.ini ]]; then - pin=$(cat /etc/raspap/networking/mobiledata.ini | sed -rn 's/pin = ([0-9]*)$/\1/p' ) - fi - $path/onoff_huawei_hilink.sh -c $mode -h $ipadd -p $pin -fi - diff --git a/includes/dashboard.php b/includes/dashboard.php index 93e640c0e..aaae31883 100755 --- a/includes/dashboard.php +++ b/includes/dashboard.php @@ -92,23 +92,13 @@ function DisplayDashboard(&$extraFooterScripts) $strTxBytes .= getHumanReadableDatasize($strTxBytes); } - // ------------------------ INFOS ABOUT THE CLIENT--------------------------------------------------------------- + // ------------------------ INFOS ABOUT THE CLIENT--------------------------------------------------------------- $clientinfo=array("name"=>"none","type"=>-1,"connected"=>"n"); $raspi_client=$_SESSION['wifi_client_interface']; load_client_config(); - $client_devs = getClients(false); - if(!empty($client_devs)) { - $ncl=$client_devs["clients"]; - if($ncl > 0) { - $ty=-1; - foreach($client_devs["device"] as $dev) { - if(($id=array_search($dev["type"],$_SESSION["net-device-types"])) > $ty && !$dev["isAP"]) { - $ty=$id; - $clientinfo=$dev; - } - } - } - } + $all_clients = getClients(false); + $clientinfo = array("name" => "none", "connected" => "n"); + if ( ($idx = findCurrentClientIndex($all_clients)) >= 0) $clientinfo = $all_clients["device"][$idx]; if ($clientinfo["name"] != "none") $raspi_client = $clientinfo["name"]; $interfaceState = $clientinfo["connected"] == "y" ? 'UP' : 'DOWN'; $txPower=""; @@ -117,7 +107,7 @@ function DisplayDashboard(&$extraFooterScripts) exec('iw dev '.$clientinfo["name"].' info | sed -rn "s/.*txpower ([0-9]*)[0-9\.]*( dBm).*/\1\2/p"', $stdoutIwInfo); if (!empty($stdoutIwInfo)) $txPower=$stdoutIwInfo[0]; } - + $classMsgDevicestatus = 'warning'; if ($interfaceState === 'UP') { $classMsgDevicestatus = 'success'; @@ -127,7 +117,8 @@ function DisplayDashboard(&$extraFooterScripts) // Pressed stop button if ($interfaceState === 'UP') { $status->addMessage(sprintf(_('Interface is going %s.'), _('down')), 'warning'); - exec('sudo /usr/local/sbin/switchClientState.sh down'); + setClientState("down"); +// exec('sudo /usr/local/sbin/switchClientState.sh down'); $status->addMessage(sprintf(_('Interface is now %s.'), _('down')), 'success'); } elseif ($interfaceState === 'unknown') { $status->addMessage(_('Interface state unknown.'), 'danger'); @@ -138,7 +129,8 @@ function DisplayDashboard(&$extraFooterScripts) // Pressed start button if ($interfaceState === 'DOWN') { $status->addMessage(sprintf(_('Interface is going %s.'), _('up')), 'warning'); - exec('sudo /usr/local/sbin/switchClientState.sh up'); + setClientState("up"); +// exec('sudo /usr/local/sbin/switchClientState.sh up'); exec('sudo ip -s a f label ' . $raspi_client); $status->addMessage(sprintf(_('Interface is now %s.'), _('up')), 'success'); } elseif ($interfaceState === 'unknown') { @@ -151,8 +143,6 @@ function DisplayDashboard(&$extraFooterScripts) } } - - // brought in from template $arrHostapdConf = parse_ini_file(RASPI_CONFIG.'/hostapd.ini'); $bridgedEnable = $arrHostapdConf['BridgedEnable']; @@ -162,7 +152,7 @@ function DisplayDashboard(&$extraFooterScripts) $client_interface = $clientinfo["name"]; } $apInterface = $_SESSION['ap_interface']; - $clientInterface = $raspi_client; + $clientInterface = $raspi_client; $MACPattern = '"([[:xdigit:]]{2}:){5}[[:xdigit:]]{2}"'; if (getBridgedState()) { $moreLink = "hostapd_conf"; @@ -174,7 +164,7 @@ function DisplayDashboard(&$extraFooterScripts) $ifaceStatus = $clientinfo["connected"]=="y" ? "up" : "down"; switch($clientinfo["type"]) { case "eth": - $client_title = "Client: Ethernet cable"; + $client_title = "Client: Ethernet cable"; $type_name = "Ethernet"; break; case "phone": @@ -198,8 +188,8 @@ function DisplayDashboard(&$extraFooterScripts) echo renderTemplate( "dashboard", compact( "clients", - "client_title", - "type_name", + "client_title", + "type_name", "moreLink", "apInterface", "clientInterface", @@ -215,7 +205,7 @@ function DisplayDashboard(&$extraFooterScripts) "strTxPackets", "strTxBytes", "txPower", - "clientinfo" + "clientinfo" ) ); $extraFooterScripts[] = array('src'=>'app/js/dashboardchart.js', 'defer'=>false); diff --git a/includes/get_clients.php b/includes/get_clients.php index b04610a47..d75a4a0dd 100644 --- a/includes/get_clients.php +++ b/includes/get_clients.php @@ -128,7 +128,10 @@ function getClients($simple=true) { $cl["device"][$i]["connected"] = "y"; $cl["device"][$i]["wan_ip"] = $ipadd[0]; } - else $cl["device"][$i]["connected"] = "n"; + else { + $cl["device"][$i]["connected"] = "n"; + $cl["device"][$i]["wan_ip"] = "-"; + } unset($res); exec("$path/info_huawei.sh operator hilink $apiadd",$res); $cl["device"][$i]["operator"] = $res[0]; @@ -170,5 +173,52 @@ function load_client_config() { } } -?> +function findCurrentClientIndex($clients) { + $devid = -1; + if(!empty($clients)) { + $ncl=$clients["clients"]; + if($ncl > 0) { + $ty=-1; + foreach($clients["device"] as $i => $dev) { + if(($id=array_search($dev["type"],$_SESSION["net-device-types"])) > $ty && !$dev["isAP"]) { + $ty=$id; + $devid=$i; + } + } + } + } + return $devid; +} +function setClientState($state) { + $clients=getClients(); + if ( ($idx = findCurrentClientIndex($clients)) >= 0) { + $dev = $clients["device"][$idx]; + exec('ifconfig -a | grep -i '.$dev["name"].' -A 1 | grep -oP "(?<=inet )([0-9]{1,3}\.){3}[0-9]{1,3}"',$res); + if ( !empty($res)) $connected=$res[0]; + switch($dev["type"]) { + case "wlan": + if($state =="up") exec('sudo ip link set '.$dev["name"].' up'); + if(!empty($connected) && $state =="down") exec('sudo ip link set '.$dev["name"].' down'); + break; + case "hilink": + preg_match("/^([0-9]{1,3}\.){3}/",$connected,$ipadd); + $ipadd = $ipadd[0].'1'; // ip address of the Hilink api + $mode = ($state == "up") ? 1 : 0; + if (file_exists(RASPI_CONFIG."/networking/mobiledata.ini")) { + $dat = parse_ini_file(RASPI_CONFIG."/networking/mobiledata.ini"); + $pin = (isset($dat["pin"]) && preg_match("/^[0-9]*$/",$dat["pin"])) ? $dat["pin"] : ""; + exec('sudo '.RASPI_CLIENT_SCRIPT_PATH.'/onoff_huawei_hilink.sh -c '.$mode.' -h '.$ipadd.' -p '.$pin); + } + break; + case "ppp": + if($state == "up") exec('ipup '.$dev["name"]); + if(!empty($connected) && $state == "down") exec('ipup '.$dev["name"]); + break; + default: + break; + } + } +} + +?> diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index 0d5e00a16..246deefaa 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -30,6 +30,8 @@ www-data ALL=(ALL) NOPASSWD:/sbin/reboot www-data ALL=(ALL) NOPASSWD:/sbin/ip link set wlan[0-9] down www-data ALL=(ALL) NOPASSWD:/sbin/ip link set wlan[0-9] up www-data ALL=(ALL) NOPASSWD:/sbin/ip -s a f label wlan[0-9] +www-data ALL=(ALL) NOPASSWD:/sbin/ifup * +www-data ALL=(ALL) NOPASSWD:/sbin/ifdown * www-data ALL=(ALL) NOPASSWD:/bin/cp /etc/raspap/networking/dhcpcd.conf /etc/dhcpcd.conf www-data ALL=(ALL) NOPASSWD:/etc/raspap/hostapd/enablelog.sh www-data ALL=(ALL) NOPASSWD:/etc/raspap/hostapd/disablelog.sh @@ -43,8 +45,7 @@ www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/dnsmasqdata /etc/dnsmasq.d/090_adblock. www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/dnsmasq_custom /etc/raspap/adblock/custom.txt 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/switchClientState.sh up -www-data ALL=(ALL) NOPASSWD:/usr/local/sbin/switchClientState.sh down +www-data ALL=(ALL) NOPASSWD:/usr/local/sbin/onoff_huawei_hilink.sh * 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 diff --git a/templates/dashboard.php b/templates/dashboard.php index bf841b599..77e462afa 100755 --- a/templates/dashboard.php +++ b/templates/dashboard.php @@ -35,35 +35,36 @@

+ -
-
-
-
+
+
+
+
-
+
-
-
+
+
- -
-
-
-
-
+
+
+
+
+
'.$gw.""; ?>
-
-
-
-
+
+
+
+
-
-
+
+
From 3ab90f64b7ae81131c9817bbb4e31c29358b73cb Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Fri, 12 Mar 2021 15:55:43 +0100 Subject: [PATCH 118/300] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3cdbbc689..6a974b350 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ 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 @@ -34,7 +34,7 @@ When backers were asked which feature they'd most like to see added to RaspAP, t ✅ 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. From 438ab9430949355c7aaaa08bc5884defa06878b8 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Sun, 14 Mar 2021 18:02:19 +0100 Subject: [PATCH 119/300] Fix access token option in raspbian.sh --- installers/raspbian.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/installers/raspbian.sh b/installers/raspbian.sh index 230f4887e..0e3c8611c 100755 --- a/installers/raspbian.sh +++ b/installers/raspbian.sh @@ -88,6 +88,7 @@ function _parse_params() { ;; -t|--token) acctoken="$2" + shift ;; -v|--version) _version From c87253a06b0b839307143ff48f7ca5cc55b4697c Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Sun, 14 Mar 2021 19:53:08 +0100 Subject: [PATCH 120/300] Fix SSID consisting of multiple words --- includes/get_clients.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/get_clients.php b/includes/get_clients.php index d75a4a0dd..b91633b97 100644 --- a/includes/get_clients.php +++ b/includes/get_clients.php @@ -64,7 +64,7 @@ function getClients($simple=true) { $cl["device"][$i]["isAP"] = !empty($retiw); unset($retiw); exec("iw dev $dev link 2> /dev/null",$retiw); - if(!$simple && !empty($ssid=preg_only_match("/.*SSID: (\w*).*/",$retiw)) ) { + if(!$simple && !empty($ssid=preg_only_match("/.*SSID: ([\w ]*).*/",$retiw)) ) { $cl["device"][$i]["connected"] = "y"; $cl["device"][$i]["ssid"] = $ssid; $cl["device"][$i]["ap-mac"] = preg_only_match("/^Connected to ([0-9a-f\:]*).*$/",$retiw); @@ -212,8 +212,8 @@ function setClientState($state) { } break; case "ppp": - if($state == "up") exec('ipup '.$dev["name"]); - if(!empty($connected) && $state == "down") exec('ipup '.$dev["name"]); + if($state == "up") exec('sudo ifup '.$dev["name"]); + if(!empty($connected) && $state == "down") exec('sudo ifdown '.$dev["name"]); break; default: break; From 2b3d37a68ab0b2df940c15078317314dab436447 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 14 Mar 2021 19:27:13 +0000 Subject: [PATCH 121/300] Fix for -t,--token option, thx @zbchristian --- installers/raspbian.sh | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/installers/raspbian.sh b/installers/raspbian.sh index fed9b6196..f340b638a 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 # @@ -45,7 +45,7 @@ function _main() { } function _parse_params() { - # default flag values + # default option values assume_yes=0 upgrade=0 ovpn_option=1 @@ -89,6 +89,7 @@ function _parse_params() { ;; -t|--token) acctoken="$2" + shift ;; -v|--version) _version @@ -132,11 +133,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 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 @@ -173,7 +174,7 @@ 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_RELEASE}" + 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" } @@ -182,10 +183,10 @@ function _display_welcome() { function _get_release() { readonly RASPAP_LATEST=$(curl -s "https://api.github.com/repos/$repo/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")' ) if [ "$insiders" == 1 ]; then - RASPAP_INSIDERS_LATEST=$(curl -s "https://install.raspap.com/repos/RaspAP/raspap-insiders/releases/latest/" | grep -Po '"tag_name": "\K.*?(?=")' ) - RASPAP_RELEASE="${RASPAP_INSIDERS_LATEST} Insiders" + 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 - RASPAP_RELEASE="${RASPAP_LATEST}" + readonly RASPAP_RELEASE="${RASPAP_LATEST}" fi } @@ -221,7 +222,6 @@ function _update_system_packages() { # Fetch required installer functions function _load_installer() { - # fetch latest release tag _get_release @@ -230,11 +230,13 @@ function _load_installer() { branch=$RASPAP_LATEST fi - UPDATE_URL="https://raw.githubusercontent.com/$repo/$branch/" + # 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/" if [ "${install_cert:-}" = 1 ]; then source="mkcert" wget "${header[@]}" -q ${UPDATE_URL}installers/${source}.sh -O /tmp/raspap_${source}.sh @@ -242,7 +244,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 From ee634c4b50f8032290bc35e26655234b80006667 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 15 Mar 2021 10:11:36 +0000 Subject: [PATCH 122/300] Initial commit: ajax fetch wg client.conf --- ajax/networking/get_wgcfg.php | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 ajax/networking/get_wgcfg.php diff --git a/ajax/networking/get_wgcfg.php b/ajax/networking/get_wgcfg.php new file mode 100644 index 000000000..6a9d771de --- /dev/null +++ b/ajax/networking/get_wgcfg.php @@ -0,0 +1,9 @@ + Date: Mon, 15 Mar 2021 10:12:18 +0000 Subject: [PATCH 123/300] Add handler for wg client.conf download --- app/js/custom.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/js/custom.js b/app/js/custom.js index b2214c9c1..72f363de9 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -361,6 +361,26 @@ $('.wg-keygen').click(function(){ }) }) +// Handler for wireguard client.conf download +$('.wg-client-dl').click(function(){ + var req = new XMLHttpRequest(); + var url = 'ajax/networking/get_wgcfg.php'; + req.open('get', url, true); + req.responseType = 'blob'; + req.setRequestHeader('Content-type', 'text/plain; charset=UTF-8'); + console.log(req); + req.onreadystatechange = function (event) { + if(req.readyState == 4 && req.status == 200) { + var blob = req.response; + var link=document.createElement('a'); + link.href=window.URL.createObjectURL(blob); + link.download = 'client.conf'; + link.click(); + } + } + req.send(); +}) + // Event listener for Bootstrap's form validation window.addEventListener('load', function() { // Fetch all the forms we want to apply custom Bootstrap validation styles to From a89140435b570449ddec99c181a076773ec2aab4 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 15 Mar 2021 10:30:36 +0000 Subject: [PATCH 124/300] Update peer template w/ download button --- templates/wg/peers.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/templates/wg/peers.php b/templates/wg/peers.php index 319cc0f99..d43475754 100644 --- a/templates/wg/peers.php +++ b/templates/wg/peers.php @@ -36,7 +36,7 @@
-
+
@@ -65,10 +65,14 @@
-
+
RaspAP Wifi QR code -
+
+ + client.conf file to your device."); ?> +
+
From 319f917071ffb05fe2dd0f8d785fafc2ff6a942c Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 15 Mar 2021 10:31:17 +0000 Subject: [PATCH 125/300] Update w/ wg download msgs --- locale/en_US/LC_MESSAGES/messages.po | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/locale/en_US/LC_MESSAGES/messages.po b/locale/en_US/LC_MESSAGES/messages.po index dcd00a5cc..746273781 100644 --- a/locale/en_US/LC_MESSAGES/messages.po +++ b/locale/en_US/LC_MESSAGES/messages.po @@ -909,6 +909,12 @@ 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" From c7c8eacb0ceea8a64894897b5d3c58a93e511fc7 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 15 Mar 2021 10:38:14 +0000 Subject: [PATCH 126/300] Minor: remove debug output --- app/js/custom.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/js/custom.js b/app/js/custom.js index 72f363de9..d50475584 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -368,7 +368,6 @@ $('.wg-client-dl').click(function(){ req.open('get', url, true); req.responseType = 'blob'; req.setRequestHeader('Content-type', 'text/plain; charset=UTF-8'); - console.log(req); req.onreadystatechange = function (event) { if(req.readyState == 4 && req.status == 200) { var blob = req.response; From 11e2724afa8c2fc96551986753c0e9eca5f53223 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Mon, 15 Mar 2021 13:29:16 +0100 Subject: [PATCH 127/300] Dashboard: show correct status after switching the client state fix huawei hilink service (udev rule) --- ajax/networking/save_net_dev_config.php | 2 +- includes/dashboard.php | 50 ++++++++++--------------- installers/raspap.sudoers | 3 +- 3 files changed, 22 insertions(+), 33 deletions(-) diff --git a/ajax/networking/save_net_dev_config.php b/ajax/networking/save_net_dev_config.php index af79cfb4d..c718ae9be 100644 --- a/ajax/networking/save_net_dev_config.php +++ b/ajax/networking/save_net_dev_config.php @@ -71,7 +71,7 @@ if (!empty($rule) ) exec('echo \''.$rule.'\' | sudo /usr/bin/tee -a '.$udevfile); } $ret=print_r($ret,true); - $jsonData = ['return'=>0,'output'=>['Udev rules changed for device '.$dev ] ]; + $jsonData = ['return'=>0,'output'=>['Settings changed for device '.$dev ] ]; echo json_encode($jsonData); return; } diff --git a/includes/dashboard.php b/includes/dashboard.php index aaae31883..399342399 100755 --- a/includes/dashboard.php +++ b/includes/dashboard.php @@ -24,6 +24,24 @@ function DisplayDashboard(&$extraFooterScripts) $status->showMessages(); return; } + + // ------------------------- Button pressed to switch client on/off --------------------------------------------------------- + $switchedOn = false; + if (!RASPI_MONITOR_ENABLED) { + if (isset($_POST['ifdown_wlan0'])) { + // Pressed stop button + $status->addMessage(sprintf(_('Interface is going %s.'), _('down')), 'warning'); + setClientState("down"); + $status->addMessage(sprintf(_('Interface is now %s.'), _('down')), 'success'); + } elseif (isset($_POST['ifup_wlan0'])) { + // Pressed start button + $status->addMessage(sprintf(_('Interface is going %s.'), _('up')), 'warning'); + setClientState("up"); + $status->addMessage(sprintf(_('Interface is now %s.'), _('up')), 'success'); + $switchedOn = true; + } + } + // ----------------------------- INFOS ABOUT THE ACCESS POINT ------------------------------------------------------------- @@ -112,36 +130,8 @@ function DisplayDashboard(&$extraFooterScripts) if ($interfaceState === 'UP') { $classMsgDevicestatus = 'success'; } - if (!RASPI_MONITOR_ENABLED) { - if (isset($_POST['ifdown_wlan0'])) { - // Pressed stop button - if ($interfaceState === 'UP') { - $status->addMessage(sprintf(_('Interface is going %s.'), _('down')), 'warning'); - setClientState("down"); -// exec('sudo /usr/local/sbin/switchClientState.sh down'); - $status->addMessage(sprintf(_('Interface is now %s.'), _('down')), 'success'); - } elseif ($interfaceState === 'unknown') { - $status->addMessage(_('Interface state unknown.'), 'danger'); - } else { - $status->addMessage(sprintf(_('Interface already %s.'), _('down')), 'warning'); - } - } elseif (isset($_POST['ifup_wlan0'])) { - // Pressed start button - if ($interfaceState === 'DOWN') { - $status->addMessage(sprintf(_('Interface is going %s.'), _('up')), 'warning'); - setClientState("up"); -// exec('sudo /usr/local/sbin/switchClientState.sh up'); - exec('sudo ip -s a f label ' . $raspi_client); - $status->addMessage(sprintf(_('Interface is now %s.'), _('up')), 'success'); - } elseif ($interfaceState === 'unknown') { - $status->addMessage(_('Interface state unknown.'), 'danger'); - } else { - $status->addMessage(sprintf(_('Interface already %s.'), _('up')), 'warning'); - } - } else { - $status->addMessage(sprintf(_('Interface is %s.'), strtolower($interfaceState)), $classMsgDevicestatus); - } - } + + if ($switchedOn) exec('sudo ip -s a f label ' . $raspi_client); // brought in from template $arrHostapdConf = parse_ini_file(RASPI_CONFIG.'/hostapd.ini'); diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index 246deefaa..4c3b5b68c 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -49,5 +49,4 @@ www-data ALL=(ALL) NOPASSWD:/usr/local/sbin/onoff_huawei_hilink.sh * 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 * From 3b64697b4af3a3b2c8f598bf6fef32130a4c7dd7 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Mon, 15 Mar 2021 14:03:34 +0100 Subject: [PATCH 128/300] Switch client on: wait for connection w/ timeout add new switchClientState.sh to be used in hilink service (calls the php getClients code) --- config/client_config/switchClientState.sh | 34 +++++++++++++++++++++++ includes/get_clients.php | 10 +++++++ 2 files changed, 44 insertions(+) create mode 100644 config/client_config/switchClientState.sh diff --git a/config/client_config/switchClientState.sh b/config/client_config/switchClientState.sh new file mode 100644 index 000000000..544fe30a5 --- /dev/null +++ b/config/client_config/switchClientState.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# start with "sudo" +# parameters: up or on +# +# switch client state to UP +# the actual code is in PHP + +# get webroot +webroot=$(cat /etc/lighttpd/lighttpd.conf | sed -rn 's/server.document-root\s*=\s*\"(.*)\"\s*$/\1/p') +if [ -z "$webroot" ] || [ ! -d "$webroot" ]; then + exit +fi +cd $webroot + +state="" +if [ ! -z $1 ] && [[ $1 =~ ^(up|on|UP|ON)$ ]]; then + state="up" +elif [ ! -z $1 ] && [[ $1 =~ ^(down|off|DOWN|OFF)$ ]]; then + state="down" +fi + +[ -z "$state" ] && exit + +php << _EOF_ + +_EOF_ + + diff --git a/includes/get_clients.php b/includes/get_clients.php index b91633b97..f8a43c4a9 100644 --- a/includes/get_clients.php +++ b/includes/get_clients.php @@ -190,6 +190,15 @@ function findCurrentClientIndex($clients) { return $devid; } +function waitClientConnected($dev, $timeout=10) { + do { + exec('ifconfig -a | grep -i '.$dev.' -A 1 | grep -oP "(?<=inet )([0-9]{1,3}\.){3}[0-9]{1,3}"',$res); + $connected= !empty($res); + if(!$connected) sleep(1); + } while(!$connected && --$timeout > 0); + return $connected; +} + function setClientState($state) { $clients=getClients(); if ( ($idx = findCurrentClientIndex($clients)) >= 0) { @@ -218,6 +227,7 @@ function setClientState($state) { default: break; } + if($state=="up") waitClientConnected($dev["name"],15); } } From 8bccf1d07721380a9d2eda19395f0822c43e72c1 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Mon, 15 Mar 2021 18:07:14 +0100 Subject: [PATCH 129/300] Add modal message when start/stop client interface is pressed --- templates/dashboard.php | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/templates/dashboard.php b/templates/dashboard.php index 77e462afa..acadda957 100755 --- a/templates/dashboard.php +++ b/templates/dashboard.php @@ -135,15 +135,15 @@
- + - " name="ifup_wlan0" /> + " name="ifup_wlan0" data-toggle="modal" data-target="#switchClientModal"/> - " name="ifdown_wlan0" /> + " name="ifdown_wlan0" data-toggle="modal" data-target="#switchClientModal"/> -
@@ -152,6 +152,21 @@
+ + + + +
+ + +
+ +
+
+
From 6eb51a2d63d95e6f603fc2bf1f6511e7ee4f414f Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Sun, 28 Mar 2021 14:44:05 +0200 Subject: [PATCH 152/300] Add flag for client configuration --- includes/dashboard.php | 7 +++++-- templates/dashboard.php | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/includes/dashboard.php b/includes/dashboard.php index 7bfbeef5f..a53aa820a 100755 --- a/includes/dashboard.php +++ b/includes/dashboard.php @@ -152,6 +152,7 @@ function DisplayDashboard(&$extraFooterScripts) exec('cat ' . RASPI_DNSMASQ_LEASES . '| grep -E $(iw dev ' . $apInterface . ' station dump | grep -oE ' . $MACPattern . ' | paste -sd "|")', $clients); } $ifaceStatus = $clientinfo["connected"]=="y" ? "up" : "down"; + $isClientConfigured = true; switch($clientinfo["type"]) { case "eth": case "usb": @@ -174,7 +175,8 @@ function DisplayDashboard(&$extraFooterScripts) default: $client_title = "No information available"; $type_name = "Not configured"; - $ifaceStatus = "warn"; + $ifaceStatus = "warn"; + $isClientConfigured = false; } echo renderTemplate( @@ -197,7 +199,8 @@ function DisplayDashboard(&$extraFooterScripts) "strTxPackets", "strTxBytes", "txPower", - "clientinfo" + "clientinfo", + "isClientConfigured" ) ); $extraFooterScripts[] = array('src'=>'app/js/dashboardchart.js', 'defer'=>false); diff --git a/templates/dashboard.php b/templates/dashboard.php index 59d08605c..0b3b90c28 100755 --- a/templates/dashboard.php +++ b/templates/dashboard.php @@ -9,7 +9,7 @@
@@ -69,7 +69,7 @@
- +
Date: Sun, 28 Mar 2021 16:35:19 +0200 Subject: [PATCH 153/300] Add code for PPP and TUN devices --- includes/internetRoute.php | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/includes/internetRoute.php b/includes/internetRoute.php index 679131973..bbb090f7c 100755 --- a/includes/internetRoute.php +++ b/includes/internetRoute.php @@ -11,10 +11,23 @@ function getRouteInfo($checkAccess) $rInfo = array(); // get all default routes exec('ip route list | sed -rn "s/default via (([0-9]{1,3}\.){3}[0-9]{1,3}).*dev (\w*).*src (([0-9]{1,3}\.){3}[0-9]{1,3}).*/\3 \4 \1/p"', $routes); - exec('ip route list | sed -rn "s/default dev (\w*) scope link/\1/p"', $devs); + $devpat = array("tun", "ppp"); // routing in case of VPN and PPP connection are different + foreach ($devpat as $pat) { + exec('ip route list | grep -oP "'.$pat.'[0-9]" | sort -u', $devs); + } if (!empty($devs)) { - foreach ($devs as $dev) - exec('ip route list | sed -rn "s/(([0-9]{1,3}\.){3}[0-9]{1,3}).*dev.*("' . $dev . '").*scope link src (([0-9]{1,3}\.){3}[0-9]{1,3}).*/\3 \4 \1/p"', $routes); + foreach ($devs as $dev) { + unset($gateway); + unset($ipadd); + exec('ip route list | sed -rn "s/^.*via (([0-9]{1,3}\.){3}[0-9]{1,3}) dev "' . $dev . '".*$/\1/p" | head -n 1', $gateway); + if (empty($gateway)) { + exec('ip route list | sed -rn "s/(([0-9]{1,3}\.){3}[0-9]{1,3}).*dev.*"' . $dev . '".*scope link src.*/\1/p"', $gateway); + } + exec('ifconfig -a | grep -i ' . $dev . ' -A 1 | grep -oP "(?<=inet )([0-9]{1,3}\.){3}[0-9]{1,3}"', $ipadd); + if (!empty($gateway) && !empty($ipadd)) { + $routes[]="$dev $ipadd[0] $gateway[0]"; + } + } } if (!empty($routes)) { foreach ($routes as $i => $route) { @@ -33,7 +46,7 @@ function getRouteInfo($checkAccess) $rInfo[$i]["access-ip"] = empty($okip) ? false : true; unset($okdns); exec('ping -W1 -c 1 -I ' . $prop[0] . ' ' . RASPI_ACCESS_CHECK_DNS . ' | sed -rn "s/.*icmp_seq=1.*time=.*/OK/p"', $okdns); - $rInfo[$i]["access-dns"] = empty($okdns) ? false : true; + $rInfo[$i]["access-dns"] = empty($okdns) ? false : true; } } } else { From 300f2c24570d08229fc4d9d3cb4be4c646451e2d Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Mon, 29 Mar 2021 15:16:23 +0200 Subject: [PATCH 154/300] Cleanup and fix for udev rules written for clients Fix device type in client table --- ajax/networking/save_net_dev_config.php | 126 ++++++++++++------------ includes/get_clients.php | 33 +++++-- includes/internetRoute.php | 2 +- templates/networking.php | 2 +- 4 files changed, 89 insertions(+), 74 deletions(-) diff --git a/ajax/networking/save_net_dev_config.php b/ajax/networking/save_net_dev_config.php index c718ae9be..e90cd1ea1 100644 --- a/ajax/networking/save_net_dev_config.php +++ b/ajax/networking/save_net_dev_config.php @@ -17,76 +17,78 @@ $file = $int.".ini"; $cfgfile="/etc/wvdial.conf"; if ( $int == "mobiledata") { - $cfg['pin'] = $_POST["pin-mobile"]; - $cfg['apn'] = $_POST["apn-mobile"]; - $cfg['apn_user'] = $_POST["apn-user-mobile"]; - $cfg['apn_pw'] = $_POST["apn-pw-mobile"]; - if (file_exists($cfgfile)) { - if($cfg["pin"] !== "") exec('sudo /bin/sed -i "s/CPIN=\".*\"/CPIN=\"'.$cfg["pin"].'\"/gi" '.$cfgfile); - if($cfg["apn"] !== "") exec('sudo /bin/sed -i "s/\"IP\"\,\".*\"/\"IP\"\,\"'.$cfg["apn"].'\"/gi" '.$cfgfile); - if($cfg["apn_user"] !== "") exec('sudo /bin/sed -i "s/^username = .*$/Username = '.$cfg["apn_user"].'/gi" '.$cfgfile); - if($cfg["apn_pw"] !== "") exec('sudo /bin/sed -i "s/^password = .*$/Password = '.$cfg["apn_pw"].'/gi" '.$cfgfile); - } + $cfg['pin'] = $_POST["pin-mobile"]; + $cfg['apn'] = $_POST["apn-mobile"]; + $cfg['apn_user'] = $_POST["apn-user-mobile"]; + $cfg['apn_pw'] = $_POST["apn-pw-mobile"]; + if (file_exists($cfgfile)) { + if($cfg["pin"] !== "") exec('sudo /bin/sed -i "s/CPIN=\".*\"/CPIN=\"'.$cfg["pin"].'\"/gi" '.$cfgfile); + if($cfg["apn"] !== "") exec('sudo /bin/sed -i "s/\"IP\"\,\".*\"/\"IP\"\,\"'.$cfg["apn"].'\"/gi" '.$cfgfile); + if($cfg["apn_user"] !== "") exec('sudo /bin/sed -i "s/^username = .*$/Username = '.$cfg["apn_user"].'/gi" '.$cfgfile); + if($cfg["apn_pw"] !== "") exec('sudo /bin/sed -i "s/^password = .*$/Password = '.$cfg["apn_pw"].'/gi" '.$cfgfile); + } } else if ( preg_match("/netdevices/",$int)) { if(!isset($_POST['opts']) ) { - $jsonData = ['return'=>0,'output'=>['No valid data to add/delete udev rule ']]; - echo json_encode($jsonData); - return; + $jsonData = ['return'=>0,'output'=>['No valid data to add/delete udev rule ']]; + echo json_encode($jsonData); + return; } else { - $opts=explode(" ",$_POST['opts'] ); - $dev=$opts[0]; - $vid=$_POST["int-vid-".$dev]; - $pid=$_POST["int-pid-".$dev]; - $mac=$_POST["int-mac-".$dev]; - $name=trim($_POST["int-name-".$dev]); - $type=$_POST["int-type-".$dev]; - $newtype=$_POST["int-new-type-".$dev]; - $udevfile=$_SESSION["udevrules"]["udev_rules_file"]; - // $udevfile="/etc/udev/rules.d/80-net-devices.rules"; + $opts=explode(" ",$_POST['opts'] ); + $dev=$opts[0]; + $vid=$_POST["int-vid-".$dev]; + $pid=$_POST["int-pid-".$dev]; + $mac=$_POST["int-mac-".$dev]; + $name=trim($_POST["int-name-".$dev]); + $name=preg_replace("/[^a-z0-9]/", "", strtolower($name)); + $type=$_POST["int-type-".$dev]; + $newtype=$_POST["int-new-type-".$dev]; + $udevfile=$_SESSION["udevrules"]["udev_rules_file"]; // default file /etc/udev/rules.d/80-net-devices.rules"; + + // find the rule prototype and prefix + $rule = ""; + foreach($_SESSION["udevrules"]["network_devices"] as $devt) { + if($devt["type"]==$newtype) { + $rulenew = $devt["udev_rule"]; + $prefix = $devt["name_prefix"]; + } + } - // find the rule prototype and prefix - $rule = ""; - foreach($_SESSION["udevrules"]["network_devices"] as $devt) { - if($devt["type"]==$newtype) { - $rule = $devt["udev_rule"]; - $prefix = $devt["name_prefix"]; - } - } - if(!empty($mac)) $rule = preg_replace("/\\\$MAC\\\$/i",$mac,$rule); - if(!empty($vid)) $rule = preg_replace("/\\\$IDVENDOR\\\$/i",$vid,$rule); - if(!empty($pid)) $rule = preg_replace("/\\\$IDPRODUCT\\\$/i",$pid,$rule); - // check for existing rule - $pre=""; - if(preg_match("/^(\w+)[0-9]$/",$dev,$match)=== 1) $pre=$match[1]; - $ruleold = preg_replace("/\\\$DEVNAME\\\$/i",$pre.".",$rule); - exec("grep -oP '".$ruleold."' $udevfile",$ret); - $newRule = empty($ret) || !empty($name); - // delete current entry - if(!empty($ret)) exec("sudo sed -i '/^".$ruleold."$/d' ".$udevfile); - exec('sudo sed -i "/^.*'.$mac.'.*$/d" '.$udevfile); - if($newRule) { + // check for an existing rule and delete lines with same MAC or same VID/PID + if (!empty($vid) && !empty($pid)) { + $rule = '^.*ATTRS{idVendor}==\"' . $vid . '\".*ATTRS{idProduct}==\"' . $pid . '\".*$'; + exec('sudo sed -i "/'.$rule.'/Id" '.$udevfile); // clear all entries with this VID/PID + $rule = '^.*ATTRS{idProduct}==\"' . $pid . '\".*ATTRS{idVendor}==\"' . $vid . '\".*$'; + exec('sudo sed -i "/'.$rule.'/Id" '.$udevfile); // clear all entries with this VID/PID + } + if (!empty($mac)) { + exec('sudo sed -i "/^.*'.$mac.'.*$/d" '.$udevfile); // clear all entries with same MAC + } // create new entry - if(empty($name)) $name = $prefix."0"; - if(!empty($name)) $rule = preg_replace("/\\\$DEVNAME\\\$/i",$name,$rule); - if (!empty($rule) ) exec('echo \''.$rule.'\' | sudo /usr/bin/tee -a '.$udevfile); - } - $ret=print_r($ret,true); - $jsonData = ['return'=>0,'output'=>['Settings changed for device '.$dev ] ]; - echo json_encode($jsonData); - return; + if ( ($type != $newtype) || !empty($name) ) { // new device type or new name + if (empty($name)) $name = $prefix."*"; + if (!empty($mac)) $rule = preg_replace("/\\\$MAC\\\$/i", $mac, $rulenew); + if (!empty($vid)) $rule = preg_replace("/\\\$IDVENDOR\\\$/i", $vid, $rule); + if (!empty($pid)) $rule = preg_replace("/\\\$IDPRODUCT\\\$/i", $pid, $rule); + if (!empty($name)) $rule = preg_replace("/\\\$DEVNAME\\\$/i",$name,$rule); + if (!empty($rule)) exec('echo \''.$rule.'\' | sudo /usr/bin/tee -a '.$udevfile); + } + $ret=print_r($ret,true); + $jsonData = ['return'=>0,'output'=>['Settings changed for device '.$dev. '
Changes will only be in effect after reconnecting the device' ] ]; + echo json_encode($jsonData); + return; } } else { - $ip = $_POST[$int.'-ipaddress']; - $netmask = mask2cidr($_POST[$int.'-netmask']); - $dns1 = $_POST[$int.'-dnssvr']; - $dns2 = $_POST[$int.'-dnssvralt']; + $ip = $_POST[$int.'-ipaddress']; + $netmask = mask2cidr($_POST[$int.'-netmask']); + $dns1 = $_POST[$int.'-dnssvr']; + $dns2 = $_POST[$int.'-dnssvralt']; - $cfg['interface'] = $int; - $cfg['routers'] = $_POST[$int.'-gateway']; - $cfg['ip_address'] = $ip."/".$netmask; - $cfg['domain_name_server'] = $dns1." ".$dns2; - $cfg['static'] = $_POST[$int.'-static']; - $cfg['failover'] = $_POST[$int.'-failover']; + $cfg['interface'] = $int; + $cfg['routers'] = $_POST[$int.'-gateway']; + $cfg['ip_address'] = $ip."/".$netmask; + $cfg['domain_name_server'] = $dns1." ".$dns2; + $cfg['static'] = $_POST[$int.'-static']; + $cfg['failover'] = $_POST[$int.'-failover']; } if (write_php_ini($cfg, RASPI_CONFIG.'/networking/'.$file)) { $jsonData = ['return'=>0,'output'=>['Successfully Updated Network Configuration']]; diff --git a/includes/get_clients.php b/includes/get_clients.php index 0fd70e830..d322c423f 100644 --- a/includes/get_clients.php +++ b/includes/get_clients.php @@ -20,16 +20,8 @@ function getClients($simple=true) } foreach ($rawdevs as $i => $dev) { $cl["device"][$i]["name"]=$dev; - if (preg_match("/^(\w+)[0-9]$/", $dev, $nam) === 1) { - $nam=$nam[1]; - } else { - $nam="none"; - } - if (($n = array_search($nam, $_SESSION["net-device-name-prefix"])) === false) { - $n = count($_SESSION["net-device-types"])-1; - } - $ty = $_SESSION["net-device-types"][$n]; - $cl["device"][$i]["type"]=$ty; + $nam = (preg_match("/^(\w+)[0-9]$/",$dev,$nam) === 1) ? $nam=$nam[1] : ""; + $cl["device"][$i]["type"]=$ty=getClientType($dev); unset($udevinfo); exec("udevadm info /sys/class/net/$dev 2> /dev/null", $udevinfo); if ($nam == "ppp" && isset($devtty)) { @@ -181,6 +173,27 @@ function getClients($simple=true) return $cl; } +function getClientType($dev) { + loadClientConfig(); + // check if device type stored in DEVTYPE or raspapType (from UDEV rule) protperty of the device + exec("udevadm info /sys/class/net/$dev 2> /dev/null", $udevadm); + $type="none"; + if (!empty($udevadm)) { + $type=preg_only_match("/raspapType=(\w*)/i",$udevadm); + if (empty($type)) { + $type=preg_only_match("/DEVTYPE=(\w*)/i",$udevadm); + } + } + if (empty($type) || array_search($type, $_SESSION["net-device-name-prefix"]) === false) { + // no device type yet -> get device type from device name + if (preg_match("/^(\w+)[0-9]$/",$dev,$nam) === 1) $nam=$nam[1]; + else $nam="none"; + if (($n = array_search($nam, $_SESSION["net-device-name-prefix"])) === false) $n = count($_SESSION["net-device-types"])-1; + $type = $_SESSION["net-device-types"][$n]; + } + return $type; +} + function loadClientConfig() { // load network device config file for UDEV rules into $_SESSION diff --git a/includes/internetRoute.php b/includes/internetRoute.php index bbb090f7c..e83dca223 100755 --- a/includes/internetRoute.php +++ b/includes/internetRoute.php @@ -46,7 +46,7 @@ function getRouteInfo($checkAccess) $rInfo[$i]["access-ip"] = empty($okip) ? false : true; unset($okdns); exec('ping -W1 -c 1 -I ' . $prop[0] . ' ' . RASPI_ACCESS_CHECK_DNS . ' | sed -rn "s/.*icmp_seq=1.*time=.*/OK/p"', $okdns); - $rInfo[$i]["access-dns"] = empty($okdns) ? false : true; + $rInfo[$i]["access-dns"] = empty($okdns) ? false : true; } } } else { diff --git a/templates/networking.php b/templates/networking.php index 78f751a09..12b997b91 100755 --- a/templates/networking.php +++ b/templates/networking.php @@ -141,7 +141,7 @@ foreach($_SESSION["net-device-types"] as $i => $type) { $txt=$_SESSION["net-device-types-info"][$i]; $txtdisabled = in_array($type,array("ppp","tun")) ? "disabled":""; - if(preg_match("/^".$_SESSION["net-device-name-prefix"][$i]."[0-9]*$/",$dev["name"])===1) echo ''; + if(preg_match("/^".$_SESSION["net-device-name-prefix"][$i].".*$/",$dev["type"])===1) echo ''; else echo ''; } echo ""; From 689b3439f7ffb5bc815598161a6359bbe7d4d887 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Mon, 29 Mar 2021 15:53:43 +0200 Subject: [PATCH 155/300] Update prototypes of udev rules with extra "raspapType" property --- config/client_udev_prototypes.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/config/client_udev_prototypes.json b/config/client_udev_prototypes.json index fe4f5249f..2cca8257d 100644 --- a/config/client_udev_prototypes.json +++ b/config/client_udev_prototypes.json @@ -9,7 +9,7 @@ "clientid": 0, "comment": "standard ethernet port", "name_prefix": "eth", - "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", ATTR{address}==\"$MAC$\", NAME=\"$DEVNAME$\" " + "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", ATTR{address}==\"$MAC$\", NAME=\"$DEVNAME$\", ENV{raspapType}="eth" " }, { "type": "usb", @@ -17,7 +17,7 @@ "clientid": 1, "comment": "network interface - e.g. USB tethering of an Android phone ", "name_prefix": "usb", - "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"$IDVENDOR$\", ATTRS{idProduct}==\"$IDPRODUCT$\", NAME=\"$DEVNAME$\" " + "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"$IDVENDOR$\", ATTRS{idProduct}==\"$IDPRODUCT$\", NAME=\"$DEVNAME$\", ENV{raspapType}="eth" " }, { "type": "wlan", @@ -25,7 +25,7 @@ "clientid": 2, "comment": "standard wireless interface", "name_prefix": "wlan", - "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", ATTR{address}==\"$MAC$\", NAME=\"$DEVNAME$\" " + "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", ATTR{address}==\"$MAC$\", NAME=\"$DEVNAME$\", ENV{raspapType}="wlan" " }, { "type": "ppp", @@ -42,7 +42,7 @@ "comment": "Huawei mobile data device in router mode. Control via HTTP", "name_prefix": "hilink", "default_ip": "192.168.8.1", - "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"$IDVENDOR$\", ATTRS{idProduct}==\"$IDPRODUCT$\", NAME=\"$DEVNAME$\", TAG+=\"systemd\", ENV{SYSTEMD_WANTS}=\"start start_huawei_hilink.service\" " + "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"$IDVENDOR$\", ATTRS{idProduct}==\"$IDPRODUCT$\", NAME=\"$DEVNAME$\", ENV{raspapType}="hilink", TAG+=\"systemd\", ENV{SYSTEMD_WANTS}=\"start start_huawei_hilink.service\" " }, { "type": "phone", @@ -50,7 +50,7 @@ "clientid": 5, "comment": "ethernet access provided by tethering from phone via USB", "name_prefix": "phone", - "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"$IDVENDOR$\", ATTRS{idProduct}==\"$IDPRODUCT$\", NAME=\"$DEVNAME$\" " + "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"$IDVENDOR$\", ATTRS{idProduct}==\"$IDPRODUCT$\", NAME=\"$DEVNAME$\", ENV{raspapType}="phone" " }, { "type": "tun", From afe0553935e959a85f039b5de8e1f9e517495309 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Mon, 29 Mar 2021 20:30:26 +0200 Subject: [PATCH 156/300] Fix EOL format --- installers/raspap.sudoers | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index 39ea76976..93389d4d2 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -59,4 +59,4 @@ 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 +www-data ALL=(ALL) NOPASSWD:/bin/rm /etc/wireguard/wg-*.key From d7428be2ec72b818daa6ec0050860bc3c49122e6 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Mon, 29 Mar 2021 21:53:26 +0200 Subject: [PATCH 157/300] Fix json format of client udev prototypes limit device name length to 20 --- ajax/networking/save_net_dev_config.php | 3 ++- config/client_udev_prototypes.json | 14 +++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/ajax/networking/save_net_dev_config.php b/ajax/networking/save_net_dev_config.php index e90cd1ea1..57c4c30ff 100644 --- a/ajax/networking/save_net_dev_config.php +++ b/ajax/networking/save_net_dev_config.php @@ -39,7 +39,9 @@ $pid=$_POST["int-pid-".$dev]; $mac=$_POST["int-mac-".$dev]; $name=trim($_POST["int-name-".$dev]); + // limit device name to letters and numbers. Total length max 20 $name=preg_replace("/[^a-z0-9]/", "", strtolower($name)); + $name=substr($name, 0, min(strlen($name),20)); $type=$_POST["int-type-".$dev]; $newtype=$_POST["int-new-type-".$dev]; $udevfile=$_SESSION["udevrules"]["udev_rules_file"]; // default file /etc/udev/rules.d/80-net-devices.rules"; @@ -72,7 +74,6 @@ if (!empty($name)) $rule = preg_replace("/\\\$DEVNAME\\\$/i",$name,$rule); if (!empty($rule)) exec('echo \''.$rule.'\' | sudo /usr/bin/tee -a '.$udevfile); } - $ret=print_r($ret,true); $jsonData = ['return'=>0,'output'=>['Settings changed for device '.$dev. '
Changes will only be in effect after reconnecting the device' ] ]; echo json_encode($jsonData); return; diff --git a/config/client_udev_prototypes.json b/config/client_udev_prototypes.json index 2cca8257d..a147e776e 100644 --- a/config/client_udev_prototypes.json +++ b/config/client_udev_prototypes.json @@ -9,7 +9,7 @@ "clientid": 0, "comment": "standard ethernet port", "name_prefix": "eth", - "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", ATTR{address}==\"$MAC$\", NAME=\"$DEVNAME$\", ENV{raspapType}="eth" " + "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", ATTR{address}==\"$MAC$\", NAME=\"$DEVNAME$\", ENV{raspapType}=\"eth\" " }, { "type": "usb", @@ -17,7 +17,7 @@ "clientid": 1, "comment": "network interface - e.g. USB tethering of an Android phone ", "name_prefix": "usb", - "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"$IDVENDOR$\", ATTRS{idProduct}==\"$IDPRODUCT$\", NAME=\"$DEVNAME$\", ENV{raspapType}="eth" " + "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"$IDVENDOR$\", ATTRS{idProduct}==\"$IDPRODUCT$\", NAME=\"$DEVNAME$\", ENV{raspapType}=\"eth\" " }, { "type": "wlan", @@ -25,7 +25,7 @@ "clientid": 2, "comment": "standard wireless interface", "name_prefix": "wlan", - "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", ATTR{address}==\"$MAC$\", NAME=\"$DEVNAME$\", ENV{raspapType}="wlan" " + "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", ATTR{address}==\"$MAC$\", NAME=\"$DEVNAME$\", ENV{raspapType}=\"wlan\" " }, { "type": "ppp", @@ -42,7 +42,7 @@ "comment": "Huawei mobile data device in router mode. Control via HTTP", "name_prefix": "hilink", "default_ip": "192.168.8.1", - "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"$IDVENDOR$\", ATTRS{idProduct}==\"$IDPRODUCT$\", NAME=\"$DEVNAME$\", ENV{raspapType}="hilink", TAG+=\"systemd\", ENV{SYSTEMD_WANTS}=\"start start_huawei_hilink.service\" " + "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"$IDVENDOR$\", ATTRS{idProduct}==\"$IDPRODUCT$\", NAME=\"$DEVNAME$\", ENV{raspapType}=\"hilink\", TAG+=\"systemd\", ENV{SYSTEMD_WANTS}=\"start start_huawei_hilink.service\" " }, { "type": "phone", @@ -50,14 +50,14 @@ "clientid": 5, "comment": "ethernet access provided by tethering from phone via USB", "name_prefix": "phone", - "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"$IDVENDOR$\", ATTRS{idProduct}==\"$IDPRODUCT$\", NAME=\"$DEVNAME$\", ENV{raspapType}="phone" " + "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"$IDVENDOR$\", ATTRS{idProduct}==\"$IDPRODUCT$\", NAME=\"$DEVNAME$\", ENV{raspapType}=\"phone\" " }, - { + { "type": "tun", "type_info": "tunnel device", "clientid": -1, "comment": "tunneling device used by OpenVPN", - "name_prefix": "tun", + "name_prefix": "tun" } ] } From 20d2443468aea115a35d9bd5ccf13a8bf1212308 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Wed, 31 Mar 2021 12:48:31 +0200 Subject: [PATCH 158/300] Fix panel title for certificate option --- templates/openvpn/general.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/templates/openvpn/general.php b/templates/openvpn/general.php index 7a33163d5..21262db1c 100644 --- a/templates/openvpn/general.php +++ b/templates/openvpn/general.php @@ -37,9 +37,8 @@
-
Enter username and password
+
Certificates in the configuration file
-
Certificates in the configuration file

RaspAP does not support the import of the required cerficates. Please paste them into the configuration file

  • Signing certification authority (CA) certificate (e.g. ca.crt): enclosed in <ca> ... </ca> tags
  • From fc83727408d9948c244954edea1fe9d04f17eb45 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Wed, 31 Mar 2021 12:54:43 +0200 Subject: [PATCH 159/300] Use client.conf to get path and name of configuration --- ajax/openvpn/activate_ovpncfg.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ajax/openvpn/activate_ovpncfg.php b/ajax/openvpn/activate_ovpncfg.php index b8f822c28..2a8542a94 100644 --- a/ajax/openvpn/activate_ovpncfg.php +++ b/ajax/openvpn/activate_ovpncfg.php @@ -5,11 +5,11 @@ if (isset($_POST['cfg_id'])) { $ovpncfg_id = $_POST['cfg_id']; - $ovpncfg_path = pathinfo(RASPI_OPENVPN_CLIENT_LOGIN, PATHINFO_DIRNAME).'/'; + $ovpncfg_path = pathinfo(RASPI_OPENVPN_CLIENT_CONFIG, PATHINFO_DIRNAME).'/'; $ovpncfg_files = $ovpncfg_path .$ovpncfg_id.'_*.conf'; // move currently active profile - $meta = file_get_meta(RASPI_OPENVPN_CLIENT_LOGIN,'#\sfilename\s(.*)'); + $meta = file_get_meta(RASPI_OPENVPN_CLIENT_CONFIG,'#\sfilename\s(.*)'); $ovpncfg_client = $ovpncfg_path .$meta.'_client.conf'; $ovpncfg_login = $ovpncfg_path .$meta.'_login.conf'; exec("sudo mv ".RASPI_OPENVPN_CLIENT_CONFIG." $ovpncfg_client", $return); From 12d52dfd3ca14d7b01727aea829fc5cc5db1558b Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Thu, 1 Apr 2021 06:58:36 +0200 Subject: [PATCH 160/300] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index df67af3ef..9ff0fe568 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![](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/) +[![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) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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. From d3c830d69f6b4bfd3b3713f72667db1e500cbd66 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 6 Apr 2021 10:34:39 +0100 Subject: [PATCH 161/300] Wrap strings w/ php gettext, update en_US locale + compile --- locale/en_US/LC_MESSAGES/messages.mo | Bin 21775 -> 23191 bytes locale/en_US/LC_MESSAGES/messages.po | 30 ++++++++++++++++++++++ templates/openvpn/general.php | 36 ++++++++++++++------------- 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/locale/en_US/LC_MESSAGES/messages.mo b/locale/en_US/LC_MESSAGES/messages.mo index d9fec98abcf58d48890136d8e32d629fd951ac0e..1a789dd42f68697f65c8d0205e7e508441d06a02 100644 GIT binary patch literal 23191 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 S;v~B2Pn<-@-v8rC^#1~zV$K}^ diff --git a/locale/en_US/LC_MESSAGES/messages.po b/locale/en_US/LC_MESSAGES/messages.po index 746273781..e715a5bec 100644 --- a/locale/en_US/LC_MESSAGES/messages.po +++ b/locale/en_US/LC_MESSAGES/messages.po @@ -725,6 +725,36 @@ msgstr "Cancel" msgid "Enable this option to log openvpn activity." msgstr "Enable this option to log openvpn activity." +msgid "Authentification Method" +msgstr "Authentification Method" + +msgid "Username and password" +msgstr "Username and password" + +msgid "Certificates" +msgstr "Certificates" + +msgid "Enter username and password" +msgstr "Enter username and password" + +msgid "Certificates in the configuration file" +msgstr "Certificates in the configuration file" + +msgid "RaspAP supports certificates by including them in the configuration file." +msgstr "RaspAP supports certificates by including them in the configuration file." + +msgid "Signing certification authority (CA) certificate (e.g. ca.crt): enclosed in <ca> ... </ca> tags." +msgstr "Signing certification authority (CA) certificate (e.g. ca.crt): enclosed in <ca> ... </ca> tags." + +msgid "Client certificate (public key) (e.g. client.crt): enclosed in <cert> ... </cert> tags." +msgstr "Client certificate (public key) (e.g. client.crt): enclosed in <cert> ... </cert> tags." + +msgid "Private key of the client certificate (e.g. client.key): enclosed in <key> ... </key> tags." +msgstr "Private key of the client certificate (e.g. client.key): enclosed in <key> ... </key> tags." + +msgid "Configuration File" +msgstr "Configuration File" + #: includes/torproxy.php msgid "TOR is not running" msgstr "TOR is not running" diff --git a/templates/openvpn/general.php b/templates/openvpn/general.php index 21262db1c..841f40857 100644 --- a/templates/openvpn/general.php +++ b/templates/openvpn/general.php @@ -11,17 +11,17 @@
    - +
    - +
    -
    Enter username and password
    +
    @@ -33,26 +33,28 @@
    -
    -
    -
    -
    -
    Certificates in the configuration file
    -
    -

    RaspAP does not support the import of the required cerficates. Please paste them into the configuration file -

      -
    • Signing certification authority (CA) certificate (e.g. ca.crt): enclosed in <ca> ... </ca> tags
    • -
    • Client certificate (public key) (e.g. client.crt): enclosed in <cert> ... </cert> tags
    • -
    • Private key of the client certificate (e.g. client.key): enclosed in <key> ... </key> tags
    • -
    -

    +
    +
    +
    +
    +
    +
    +

    +

      + +
    • ca.crt): enclosed in <ca> ... </ca> tags."); ?>
    • +
    • client.crt): enclosed in <cert> ... </cert> tags."); ?>
    • +
    • client.key): enclosed in <key> ... </key> tags."); ?>
    • +
      +
    +

    -
    Configuration File
    +
    From d221c76e32e7ea14f494ba7e00880cda33929908 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 6 Apr 2021 12:48:27 +0100 Subject: [PATCH 162/300] Spacing + remove extraneous div --- templates/openvpn.php | 3 +-- templates/openvpn/general.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/templates/openvpn.php b/templates/openvpn.php index 4f76d6ecb..4e0adaba8 100755 --- a/templates/openvpn.php +++ b/templates/openvpn.php @@ -45,8 +45,7 @@
    - -
    +
    diff --git a/templates/openvpn/general.php b/templates/openvpn/general.php index 841f40857..4cacd41a6 100644 --- a/templates/openvpn/general.php +++ b/templates/openvpn/general.php @@ -63,7 +63,7 @@
- +
From aa6df37801df13e96ad73693362f7472948a9332 Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Tue, 6 Apr 2021 14:24:36 +0200 Subject: [PATCH 163/300] Update README.md --- README.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9ff0fe568..ba9ff12de 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,12 @@ As part of the initial rollout of Insiders, all previous one-time backers of Ras ## Exclusive features 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 +✅ [Manage OpenVPN client configs](https://docs.raspap.com/openvpn/#multiple-client-configs) +✅ [OpenVPN certificate authentication](https://docs.raspap.com/openvpn/#certificate-authentication) ✅ OpenVPN service logging ✅ Night mode toggle ✅ Restrict network to static clients -✅ WireGuard support +✅ [WireGuard support](https://docs.raspap.com/wireguard/) ⚙️ 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/RaspAP/raspap-insiders/discussions) and let us know! @@ -40,12 +41,7 @@ Look for the list above to grow as we add more exlcusive features. Have an idea 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. ### $500 -✅ Manage OpenVPN client configs -✅ OpenVPN service logging -✅ Night mode toggle -✅ Restrict network to static clients -✅ WireGuard support -⚙️ Traffic shaping (in progress) +The first **Insiders Edition** includes the exclusive features listed above. ## Frequently asked questions From d3c769b7484536318f7b78b4791b95978c4187fd Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 9 Apr 2021 15:05:40 +0100 Subject: [PATCH 164/300] Layout fix: dashboard client widget + openvpn ipv4 --- app/css/custom.php | 5 +++++ app/css/hackernews.css | 5 +++++ app/css/lightsout.css | 5 +++++ templates/dashboard.php | 34 ++++++++++++++++++++++++---------- templates/openvpn/general.php | 6 ++++-- 5 files changed, 43 insertions(+), 12 deletions(-) diff --git a/app/css/custom.php b/app/css/custom.php index 6726a0a8b..273a96294 100644 --- a/app/css/custom.php +++ b/app/css/custom.php @@ -124,6 +124,11 @@ color: #858796; } +.info-value { + font-size: 0.7rem; + margin-left: 0.7rem; +} + .info-item-xs { font-size: 0.7rem; margin-left: 0.3rem; diff --git a/app/css/hackernews.css b/app/css/hackernews.css index 2d76492c0..6aef443b8 100644 --- a/app/css/hackernews.css +++ b/app/css/hackernews.css @@ -155,6 +155,11 @@ ul.nav-tabs, .nav-tabs .nav-link { color: #858796; } +.info-value { + font-size: 0.7rem; + margin-left: 0.7rem; +} + .info-item-xs { font-size: 0.7rem; margin-left: 0.3rem; diff --git a/app/css/lightsout.css b/app/css/lightsout.css index cc020f223..3e8e6e76b 100644 --- a/app/css/lightsout.css +++ b/app/css/lightsout.css @@ -254,6 +254,11 @@ hr { color: #858796; } +.info-value { + font-size: 0.7rem; + margin-left: 0.7rem; +} + .info-item-xs { font-size: 0.7rem; line-height: 1.5em; diff --git a/templates/dashboard.php b/templates/dashboard.php index 47443ee8f..597dc6b5d 100755 --- a/templates/dashboard.php +++ b/templates/dashboard.php @@ -34,17 +34,31 @@

-
-
-

-

-

-

-

-

-

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
diff --git a/templates/openvpn/general.php b/templates/openvpn/general.php index 4cacd41a6..39573ce61 100644 --- a/templates/openvpn/general.php +++ b/templates/openvpn/general.php @@ -4,8 +4,10 @@
-
-
+
+
+
+
From ba948d99ddb3c6bc95a35d35eab167cf3ec54928 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 14 Apr 2021 19:58:34 +0100 Subject: [PATCH 165/300] Update install_wireguard from apt on RPi OS --- installers/common.sh | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/installers/common.sh b/installers/common.sh index 26f3e92ff..fe36303a1 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -350,18 +350,10 @@ function _prompt_install_wireguard() { # 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" + if [ "$OS" == "Debian" ]; then + echo 'deb http://ftp.debian.org/debian buster-backports main' | sudo tee /etc/apt/sources.list.d/buster-backports.list || _install_status 1 "Unable to add Debian backports repo" 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" + echo "Installing wireguard from apt" 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" From 263e326281d0eab36985dda4274c0536cc4bde9d Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 15 Apr 2021 09:00:14 +0100 Subject: [PATCH 166/300] Descriptive function name + insiders flag check --- installers/common.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/installers/common.sh b/installers/common.sh index 725664476..e3733427b 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -56,14 +56,16 @@ function _install_raspap() { _configure_networking _prompt_install_adblock _prompt_install_openvpn - _install_features + _install_mobile_clients _prompt_install_wireguard _patch_system_files _install_complete } # search for optional installation files names install_feature_*.sh -function _install_features() { +function _install_mobile_clients() { + if [ "$insiders" == 1 ]; then + echo -n "Installing support for mobile data clients" for feature in $(ls $webroot_dir/installers/install_feature_*.sh) ; do source $feature f=$(basename $feature) @@ -75,6 +77,7 @@ function _install_features() { _install_status 1 "Install file $f is missing install function $func" fi done + fi } # Prompts user to set installation options From 6741560f1a1b6780a991b059a8c503a6014a447c Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 15 Apr 2021 09:01:29 +0100 Subject: [PATCH 167/300] Merge with upstream master --- installers/raspbian.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/installers/raspbian.sh b/installers/raspbian.sh index d84966724..5c865625f 100755 --- a/installers/raspbian.sh +++ b/installers/raspbian.sh @@ -38,6 +38,7 @@ set -eo pipefail function _main() { # set defaults repo="RaspAP/raspap-webgui" # override with -r, --repo option + repo_common="$repo" _parse_params "$@" _setup_colors _log_output @@ -72,6 +73,7 @@ function _parse_params() { ;; -r|--repo|--repository) repo="$2" + repo_common="$repo" shift ;; -b|--branch) @@ -91,10 +93,6 @@ function _parse_params() { acctoken="$2" shift ;; - -t|--token) - acctoken="$2" - shift - ;; -v|--version) _version ;; @@ -188,6 +186,7 @@ function _get_release() { readonly RASPAP_LATEST=$(curl -s "https://api.github.com/repos/$repo/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")' ) if [ "$insiders" == 1 ]; then repo="RaspAP/raspap-insiders" + repo_common="RaspAP/raspap-webgui" readonly RASPAP_INSIDERS_LATEST=$(curl -s "https://api.raspap.com/repos/RaspAP/raspap-insiders/releases/latest/" | grep -Po '"tag_name": "\K.*?(?=")' ) readonly RASPAP_RELEASE="${RASPAP_INSIDERS_LATEST} Insiders" else @@ -227,7 +226,6 @@ function _update_system_packages() { # Fetch required installer functions function _load_installer() { - # fetch latest release tag _get_release @@ -242,7 +240,7 @@ function _load_installer() { header=(--header "Authorization: token $acctoken") fi - UPDATE_URL="https://raw.githubusercontent.com/$repo/$branch/" + UPDATE_URL="https://raw.githubusercontent.com/$repo_common/$branch/" header=() if [[ ! -z "$acctoken" ]]; then header=(--header "Authorization: token $acctoken") From 38c37296af5f3ddc2ffb500fac1f5cfa037d5b9e Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 15 Apr 2021 09:03:32 +0100 Subject: [PATCH 168/300] Add new messages to en_US, compile .mo --- locale/en_US/LC_MESSAGES/messages.mo | Bin 23191 -> 25603 bytes locale/en_US/LC_MESSAGES/messages.po | 99 +++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/locale/en_US/LC_MESSAGES/messages.mo b/locale/en_US/LC_MESSAGES/messages.mo index 1a789dd42f68697f65c8d0205e7e508441d06a02..707d029c0c940db483d962056e1097cd7b93ad22 100644 GIT binary patch literal 25603 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 literal 23191 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." +msgstr "Required exec function is disabled. Check if exec is not added to php disable_functions." + +msgid "Waiting for the interface to start ..." +msgstr "Waiting for the interface to start ..." + +msgid "Stop the Interface" +msgstr "Stop the Interface" + +msgid "Connection mode" +msgstr "Connection mode" + +msgid "Signal quality" +msgstr "Signal quality" + +msgid "WAN IP" +msgstr "WAN IP" + +msgid "Web-GUI" +msgstr "Web-GUI" + +msgid "Signal strength" +msgstr "Signal strength" + +msgid "No Client device or not yet configured" +msgstr "No Client device or not yet configured" + #: includes/dhcp.php msgid "DHCP server settings" msgstr "DHCP server settings" @@ -538,6 +592,51 @@ msgstr "Apply settings" msgid "Information provided by /sys/class/net" msgstr "Information provided by /sys/class/net" +msgid "Network Devices" +msgstr "Network Devices" + +msgid "Mobile Data Settings" +msgstr "Mobile Data Settings" + +msgid "Device" +msgstr "Device" + +msgid "MAC" +msgstr "MAC" + +msgid "USB vid/pid" +msgstr "USB vid/pid" + +msgid "Device type" +msgstr "Device type" + +msgid "Fixed name" +msgstr "Fixed name" + +msgid "Settings for Mobile Data Devices" +msgstr "Settings for Mobile Data Devices" + +msgid "PIN of SIM card" +msgstr "PIN of SIM card" + +msgid "APN Settings (Modem device ppp0)" +msgstr "APN Settings (Modem device ppp0)" + +msgid "Access Point Name (APN)" +msgstr "Access Point Name (APN)" + +msgid "Password" +msgstr "Password" + +msgid "Successfully Updated Network Configuration" +msgstr "Successfully Updated Network Configuration" + +msgid "Error saving network configuration to file" +msgstr "Error saving network configuration to file" + +msgid "Unable to detect interface" +msgstr "Unable to detect interface" + #: includes/system.php msgid "System Information" msgstr "System Information" From e1c2770a76430c1b69c8a06516358cc9c75e251c Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 15 Apr 2021 09:04:20 +0100 Subject: [PATCH 169/300] Minor: formatting + status text --- templates/dashboard.php | 46 +++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/templates/dashboard.php b/templates/dashboard.php index abac1fa84..e443db8df 100755 --- a/templates/dashboard.php +++ b/templates/dashboard.php @@ -39,29 +39,29 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
'.$gw.""; ?>
-
+
-
+
-
+
-
+
-
+
-
+
-
+ -
+
+
+
@@ -202,7 +204,7 @@ From dc8b39dc401a58e5422d7a33981b6a2d1a3873f6 Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 15 Apr 2021 09:05:01 +0100 Subject: [PATCH 170/300] Output tab labels with gettext() --- templates/networking.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/networking.php b/templates/networking.php index 12b997b91..98309b5be 100755 --- a/templates/networking.php +++ b/templates/networking.php @@ -13,8 +13,8 @@
From e4967e50459a52e3b0ed8560e169e6bade16bd6e Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 16 Apr 2021 13:42:51 +0100 Subject: [PATCH 171/300] Update en_US local, template + compile .mo --- locale/en_US/LC_MESSAGES/messages.mo | Bin 25603 -> 25717 bytes locale/en_US/LC_MESSAGES/messages.po | 6 ++++++ templates/networking.php | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/locale/en_US/LC_MESSAGES/messages.mo b/locale/en_US/LC_MESSAGES/messages.mo index 707d029c0c940db483d962056e1097cd7b93ad22..cd410c69d72f88d6f60dfe8efed0ca700e0a7c49 100644 GIT binary patch delta 7573 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 delta 7487 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 P`~pkky6>#^|Kk4u*!B)3 diff --git a/locale/en_US/LC_MESSAGES/messages.po b/locale/en_US/LC_MESSAGES/messages.po index 475d49f95..5713516c4 100644 --- a/locale/en_US/LC_MESSAGES/messages.po +++ b/locale/en_US/LC_MESSAGES/messages.po @@ -598,6 +598,9 @@ msgstr "Network Devices" msgid "Mobile Data Settings" msgstr "Mobile Data Settings" +msgid "Properties of network devices" +msgstr "Properties of network devices" + msgid "Device" msgstr "Device" @@ -613,6 +616,9 @@ msgstr "Device type" msgid "Fixed name" msgstr "Fixed name" +msgid "Change" +msgstr "Change" + msgid "Settings for Mobile Data Devices" msgstr "Settings for Mobile Data Devices" diff --git a/templates/networking.php b/templates/networking.php index 98309b5be..f497cb289 100755 --- a/templates/networking.php +++ b/templates/networking.php @@ -154,7 +154,7 @@ echo ''."\n"; echo ''."\n"; echo ''; - if (! $isStatic) echo 'Change'; + if (! $isStatic) echo '' ._("Change").''; echo "\n"; echo "\n"; } From 6c674537bb779d3a4cfe5b38c342f406b3e4bcf1 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 20 Apr 2021 15:19:00 +0100 Subject: [PATCH 172/300] WIP: txpower feature --- app/js/custom.js | 5 +++++ config/defaults.json | 3 +++ includes/functions.php | 7 ++++--- includes/hostapd.php | 6 ++++-- installers/raspap.sudoers | 1 + templates/hostapd/advanced.php | 12 ++++++++++++ 6 files changed, 29 insertions(+), 5 deletions(-) diff --git a/app/js/custom.js b/app/js/custom.js index d5c1abe5d..3523fc561 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -120,6 +120,11 @@ $(document).on("click", "#gen_wpa_passphrase", function(e) { $('#txtwpapassphrase').val(genPassword(63)); }); +// Enable Bootstrap tooltips +$(function () { + $('[data-toggle="tooltip"]').tooltip() +}) + function genPassword(pwdLen) { var pwdChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; var rndPass = Array(pwdLen).fill(pwdChars).map(function(x) { return x[Math.floor(Math.random() * x.length)] }).join(''); diff --git a/config/defaults.json b/config/defaults.json index faab15b6a..67948d0d7 100644 --- a/config/defaults.json +++ b/config/defaults.json @@ -49,6 +49,9 @@ "AllowedIPs": ["10.8.2.0/24"], "PersistentKeepalive": [ "15" ] } + }, + "txpower": { + "dbm": [ "auto", "30", "20", "17", "10", "6", "3", "1", "0" ] } } diff --git a/includes/functions.php b/includes/functions.php index 2f1c502bf..a3ffbf45d 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -152,15 +152,16 @@ function getDefaultNetValue($svc,$iface,$key) * Returns default options for the specified service * * @param string $svc + * @param string $key * @return object $json */ -function getDefaultNetOpts($svc) +function getDefaultNetOpts($svc,$key) { $json = json_decode(file_get_contents(RASPI_CONFIG_NETWORK), true); if ($json === null) { return false; } else { - return $json[$svc]['options']; + return $json[$svc][$key]; } } @@ -774,4 +775,4 @@ function preg_only_match($pat,$haystack) { function qr_encode($str) { return preg_replace('/(? 'WPA', 2 => 'WPA2', 3 => 'WPA+WPA2', 'none' => _("None")); $arrEncType = array('TKIP' => 'TKIP', 'CCMP' => 'CCMP', 'TKIP CCMP' => 'TKIP+CCMP'); + $arrTxPower = getDefaultNetOpts('txpower','dbm'); $managedModeEnabled = false; exec("ip -o link show | awk -F': ' '{print $2}'", $interfaces); sort($interfaces); @@ -102,6 +103,7 @@ function DisplayHostAPDConfig() "selectedHwMode", "arrSecurity", "arrEncType", + "arrTxPower", "arrHostapdConf" ) ); @@ -306,13 +308,13 @@ function SaveHostAPDConfig($wpa_array, $enc_types, $modes, $interfaces, $status) $ip_address.= (!preg_match('/.*\/\d+/', $ip_address)) ? '/'.mask2cidr($netmask) : null; if ($bridgedEnable == 1) { - $config = array_keys(getDefaultNetOpts('dhcp')); + $config = array_keys(getDefaultNetOpts('dhcp','options')); $config[] = PHP_EOL.'# RaspAP br0 configuration'; $config[] = 'denyinterfaces eth0 wlan0'; $config[] = 'interface br0'; $config[] = PHP_EOL; } elseif ($wifiAPEnable == 1) { - $config = array_keys(getDefaultNetOpts('dhcp')); + $config = array_keys(getDefaultNetOpts('dhcp','options')); $config[] = PHP_EOL.'# RaspAP uap0 configuration'; $config[] = 'interface uap0'; $config[] = 'static ip_address='.$ip_address; diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index 93389d4d2..355fd8eb9 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -32,6 +32,7 @@ www-data ALL=(ALL) NOPASSWD:/sbin/ip link set wlan[0-9] up www-data ALL=(ALL) NOPASSWD:/sbin/ip -s a f label wlan[0-9] www-data ALL=(ALL) NOPASSWD:/sbin/ifup * www-data ALL=(ALL) NOPASSWD:/sbin/ifdown * +www-data ALL=(ALL) NOPASSWD:/sbin/iw www-data ALL=(ALL) NOPASSWD:/bin/cp /etc/raspap/networking/dhcpcd.conf /etc/dhcpcd.conf www-data ALL=(ALL) NOPASSWD:/etc/raspap/hostapd/enablelog.sh www-data ALL=(ALL) NOPASSWD:/etc/raspap/hostapd/disablelog.sh diff --git a/templates/hostapd/advanced.php b/templates/hostapd/advanced.php index f7c9ee93e..579e166c1 100644 --- a/templates/hostapd/advanced.php +++ b/templates/hostapd/advanced.php @@ -1,3 +1,4 @@ +' . var_export($arrTxPower, true) . ''; ?>

@@ -52,6 +53,17 @@

+
+
+ + "> + + +
+
+
From 643afe09e8905c98f7b34dd8a1ec3a36b3d19802 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 20 Apr 2021 17:53:09 +0100 Subject: [PATCH 173/300] Set txpower w/ iw, persist value in UI --- includes/hostapd.php | 18 +++++++++++++++++- templates/hostapd/advanced.php | 3 +-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/includes/hostapd.php b/includes/hostapd.php index b87b1e672..8ed669466 100755 --- a/includes/hostapd.php +++ b/includes/hostapd.php @@ -32,6 +32,10 @@ function DisplayHostAPDConfig() exec("iw reg get | awk '/country / { sub(/:/,\"\",$2); print $2 }'", $country_code); + $cmd = "iw dev ".$_SESSION['ap_interface']." info | awk '$1==\"txpower\" {print $2}'"; + exec($cmd, $txpower); + $txpower = intval($txpower[0]); + if (!RASPI_MONITOR_ENABLED) { if (isset($_POST['SaveHostAPDSettings'])) { SaveHostAPDConfig($arrSecurity, $arrEncType, $arr80211Standard, $interfaces, $status); @@ -62,7 +66,7 @@ function DisplayHostAPDConfig() } exec('cat '. RASPI_HOSTAPD_CONFIG, $hostapdconfig); - exec('iwgetid '. $_POST['interface']. ' -r', $wifiNetworkID); + exec('iwgetid '. $_POST['interface'].' -r', $wifiNetworkID); if (!empty($wifiNetworkID[0])) { $managedModeEnabled = true; } @@ -90,6 +94,17 @@ function DisplayHostAPDConfig() if (!isset($arrConfig['country_code']) && isset($country_code[0])) { $arrConfig['country_code'] = $country_code[0]; } + // set txpower with iw if value is non-default ('auto') + if (isset($_POST['txpower']) && ($_POST['txpower'] != 'auto')) { + $sdBm = $_POST['txpower'] * 100; + exec('sudo /sbin/iw dev '.$_POST['interface'].' set txpower fixed '.$sdBm, $return); + $status->addMessage('Setting transmit power to '.$_POST['txpower'].' dBm.', 'success'); + $txpower = $_POST['txpower']; + } elseif ($_POST['txpower'] == 'auto') { + exec('sudo /sbin/iw dev '.$_POST['interface'].' set txpower auto', $return); + $status->addMessage('Setting transmit power to '.$_POST['txpower'].'.', 'success'); + $txpower = $_POST['txpower']; + } echo renderTemplate( "hostapd", compact( @@ -104,6 +119,7 @@ function DisplayHostAPDConfig() "arrSecurity", "arrEncType", "arrTxPower", + "txpower", "arrHostapdConf" ) ); diff --git a/templates/hostapd/advanced.php b/templates/hostapd/advanced.php index 579e166c1..447623f81 100644 --- a/templates/hostapd/advanced.php +++ b/templates/hostapd/advanced.php @@ -1,4 +1,3 @@ -' . var_export($arrTxPower, true) . ''; ?>

@@ -58,7 +57,7 @@ ">
From 14b6a72105ee4affce1a26cc0ea06f1ef4cfe0d8 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 20 Apr 2021 20:00:40 +0100 Subject: [PATCH 174/300] Add text-muted to tooltip, revise placement + text --- templates/hostapd/advanced.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/hostapd/advanced.php b/templates/hostapd/advanced.php index 447623f81..f331734b4 100644 --- a/templates/hostapd/advanced.php +++ b/templates/hostapd/advanced.php @@ -55,11 +55,11 @@
- "> + "> - + txpower option for the AP interface and the configured country."); ?>
@@ -67,7 +67,7 @@
" aria-describedby="max_num_sta_help"> - + max_num_sta option of hostapd. The default and maximum is 2007. If empty or 0, the default applies.") ?>
From 3ee38e6abe2dd62f8887a735c75e75a822225f29 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 20 Apr 2021 20:01:25 +0100 Subject: [PATCH 175/300] Update en_US locale w/ new messages + compile .mo --- locale/en_US/LC_MESSAGES/messages.mo | Bin 25717 -> 26387 bytes locale/en_US/LC_MESSAGES/messages.po | 13 +++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/locale/en_US/LC_MESSAGES/messages.mo b/locale/en_US/LC_MESSAGES/messages.mo index cd410c69d72f88d6f60dfe8efed0ca700e0a7c49..f51f7c62d77de2fcf26b546fbdae30f2fa79006b 100644 GIT binary patch literal 26387 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 G_5TH!Cm{U* diff --git a/locale/en_US/LC_MESSAGES/messages.po b/locale/en_US/LC_MESSAGES/messages.po index 5713516c4..a321f2b7d 100644 --- a/locale/en_US/LC_MESSAGES/messages.po +++ b/locale/en_US/LC_MESSAGES/messages.po @@ -528,8 +528,8 @@ msgstr "Hide SSID in broadcast" msgid "Maximum number of clients" msgstr "Maximum number of clients" -msgid "Configures the max_num_sta option of hostapd. The default and maximum is 2007. If empty or 0, the default applies." -msgstr "Configures the max_num_sta option of hostapd. The default and maximum is 2007. If empty or 0, the default applies." +msgid "Configures the max_num_sta option of hostapd. The default and maximum is 2007. If empty or 0, the default applies." +msgstr "Configures the max_num_sta option of hostapd. The default and maximum is 2007. If empty or 0, the default applies." msgid "Beacon interval" msgstr "Beacon interval" @@ -549,6 +549,15 @@ msgstr "Close" msgid "Enable this option to log hostapd activity." msgstr "Enable this option to log hostapd activity." +msgid "Transmit power (dBm)" +msgstr "Transmit power (dBm)" + +msgid "Sets the txpower option for the AP interface and the configured country." +msgstr "Sets the txpower option for the AP interface and the configured country." + +msgid "dBm is a unit of level used to indicate that a power ratio is expressed in decibels (dB) with reference to one milliwatt (mW). 30 dBm is equal to 1000 mW, while 0 dBm equals 1.25 mW." +msgstr "dBm is a unit of level used to indicate that a power ratio is expressed in decibels (dB) with reference to one milliwatt (mW). 30 dBm is equal to 1000 mW, while 0 dBm equals 1.25 mW." + #: includes/networking.php msgid "Summary" msgstr "Summary" From 6ac7642c335d69d22eb66a6c0c4c01a0b99d4a4f Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 23 Apr 2021 12:45:28 +0100 Subject: [PATCH 176/300] Update release version --- README.md | 4 +++- includes/defaults.php | 2 +- index.php | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ba9ff12de..64174d881 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![](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) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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/) +[![Release 2.7.3](https://img.shields.io/badge/release-v2.7.3-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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. @@ -33,6 +33,8 @@ The following features are currently available exclusively to sponsors. A tangib ✅ Night mode toggle ✅ Restrict network to static clients ✅ [WireGuard support](https://docs.raspap.com/wireguard/) +✅ [Set AP transmit power](https://docs.raspap.com/ap-basics/#transmit-power) +✅ Mobile data client 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/RaspAP/raspap-insiders/discussions) and let us know! diff --git a/includes/defaults.php b/includes/defaults.php index 34e083780..1c87e4a50 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -6,7 +6,7 @@ $defaults = [ 'RASPI_BRAND_TEXT' => 'RaspAP', - 'RASPI_VERSION' => '2.7.2', + 'RASPI_VERSION' => '2.7.3', 'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json', 'RASPI_ADMIN_DETAILS' => RASPI_CONFIG.'/raspap.auth', 'RASPI_WIFI_AP_INTERFACE' => 'wlan0', diff --git a/index.php b/index.php index 60c6088bf..4f79b0627 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.2 + * @version 2.7.3 * @link https://github.com/raspap/raspap-insiders/ * @link https://raspap.com/ * @see http://sirlagz.net/2013/02/08/raspap-webgui/ From e4995314f0ddd16771af03ea466ec723fa72f514 Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Fri, 23 Apr 2021 14:39:05 +0200 Subject: [PATCH 177/300] Update banner img --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 64174d881..8d2c15f45 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![](https://i.imgur.com/TCJKWT4.png) +![](https://i.imgur.com/FRU1tXF.png) [![Release 2.7.3](https://img.shields.io/badge/release-v2.7.3-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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. From 422ce9dd3bbcfb6919c4ade4c7f32ffff6b5a183 Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Fri, 23 Apr 2021 18:04:11 +0200 Subject: [PATCH 178/300] Fix unknown device type for mobile modems (ppp) --- includes/get_clients.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/get_clients.php b/includes/get_clients.php index d322c423f..094cb8bf9 100644 --- a/includes/get_clients.php +++ b/includes/get_clients.php @@ -184,7 +184,7 @@ function getClientType($dev) { $type=preg_only_match("/DEVTYPE=(\w*)/i",$udevadm); } } - if (empty($type) || array_search($type, $_SESSION["net-device-name-prefix"]) === false) { + if (empty($type) || $type == "none" || array_search($type, $_SESSION["net-device-name-prefix"]) === false) { // no device type yet -> get device type from device name if (preg_match("/^(\w+)[0-9]$/",$dev,$nam) === 1) $nam=$nam[1]; else $nam="none"; From 9f38f64c3d41f5d1c5738feb084db7ede1cdcb84 Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Fri, 23 Apr 2021 19:01:03 +0200 Subject: [PATCH 179/300] Add auto YES to apt-get in order to avoid abort during installation --- installers/install_feature_clients.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installers/install_feature_clients.sh b/installers/install_feature_clients.sh index fa5f5852f..e1a6ca926 100644 --- a/installers/install_feature_clients.sh +++ b/installers/install_feature_clients.sh @@ -19,7 +19,7 @@ function _install_feature_clients() { _install_log "Install $name" _install_log " - required packages for mobile data clients" - sudo apt-get install wvdial socat bc || _install_status 1 "Unable to install dependencies for $name" + sudo apt-get -y install wvdial socat bc || _install_status 1 "Unable to install dependencies for $name" _install_log " - copy configuration files and scripts" # Move scripts From 29c4c5a833e4fcd164885cc531fcb9e20df7a4b1 Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Sat, 24 Apr 2021 11:11:48 +0200 Subject: [PATCH 180/300] Fix default device name numbering in UDEV rule --- ajax/networking/save_net_dev_config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ajax/networking/save_net_dev_config.php b/ajax/networking/save_net_dev_config.php index 57c4c30ff..be2c04dd0 100644 --- a/ajax/networking/save_net_dev_config.php +++ b/ajax/networking/save_net_dev_config.php @@ -67,7 +67,7 @@ } // create new entry if ( ($type != $newtype) || !empty($name) ) { // new device type or new name - if (empty($name)) $name = $prefix."*"; + if (empty($name)) $name = $prefix."%n"; if (!empty($mac)) $rule = preg_replace("/\\\$MAC\\\$/i", $mac, $rulenew); if (!empty($vid)) $rule = preg_replace("/\\\$IDVENDOR\\\$/i", $vid, $rule); if (!empty($pid)) $rule = preg_replace("/\\\$IDPRODUCT\\\$/i", $pid, $rule); From 9d132bee3a00b34247921fda1ce1d6045ebcafe1 Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Sat, 24 Apr 2021 11:17:25 +0200 Subject: [PATCH 181/300] Fix naming of hilink devices --- config/client_config/80-raspap-net-devices.rules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/client_config/80-raspap-net-devices.rules b/config/client_config/80-raspap-net-devices.rules index 0ccb49a19..33e6584c3 100644 --- a/config/client_config/80-raspap-net-devices.rules +++ b/config/client_config/80-raspap-net-devices.rules @@ -1,3 +1,3 @@ -SUBSYSTEM=="net", ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="14db", NAME="hilink0", TAG+="systemd", ENV{SYSTEMD_WANTS}="start start_huawei_hilink.service" +SUBSYSTEM=="net", ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="14db", NAME="hilink%n", TAG+="systemd", ENV{SYSTEMD_WANTS}="start start_huawei_hilink.service" From 97b2a091455727ab6b3c212de84c26d30219dc1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 May 2021 23:37:52 +0000 Subject: [PATCH 182/300] Bump hosted-git-info from 2.8.8 to 2.8.9 Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9. - [Release notes](https://github.com/npm/hosted-git-info/releases) - [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md) - [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9) Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index fc411a7a6..0605ff2f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2010,9 +2010,9 @@ homedir-polyfill@^1.0.1: parse-passwd "^1.0.0" hosted-git-info@^2.1.4: - version "2.8.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" - integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== http-errors@1.7.3: version "1.7.3" From 45cbbea78c172569abe2121a061f32c1eba3e2ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 May 2021 00:04:42 +0000 Subject: [PATCH 183/300] Bump lodash from 4.17.20 to 4.17.21 Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21) Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index fc411a7a6..028e23570 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2584,9 +2584,9 @@ lodash.templatesettings@^4.0.0: lodash._reinterpolate "^3.0.0" lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.15, lodash@~4.17.10: - version "4.17.20" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" - integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== loud-rejection@^1.0.0: version "1.6.0" From 613cd6bae8ede03ec41e1c13fe3ebfc51498e3dd Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Wed, 19 May 2021 21:04:31 +0200 Subject: [PATCH 184/300] Add Huawei Hilink API Cleanup mobile data scripts --- config/client_config/huawei_hilink_api.sh | 482 ++++++++++++++++++++ config/client_config/info_huawei.sh | 58 +-- config/client_config/info_huawei_hilink.sh | 94 ++-- config/client_config/onoff_huawei_hilink.sh | 109 +---- 4 files changed, 582 insertions(+), 161 deletions(-) create mode 100644 config/client_config/huawei_hilink_api.sh diff --git a/config/client_config/huawei_hilink_api.sh b/config/client_config/huawei_hilink_api.sh new file mode 100644 index 000000000..c4402a6e7 --- /dev/null +++ b/config/client_config/huawei_hilink_api.sh @@ -0,0 +1,482 @@ +#!/bin/bash +# +# Huawei Hilink API +# ================= +# - communication with Hilink devices via HTTP +# - send a standard http request with a xml formatted string to the device (default IP 192.169.8.1) +# - Howto: +# o "source" this script in your own script from the command line +# o if host ip/name differs, set "host=192.168.178.1" before calling any function +# o if the device is locked by a password, set user="admin"; pw="1234secret" +# _login is called automaticallcall +# Password types 3 and 4 are supported +# o if the SIM is requiring a PIN, set "pin=1234" +# o connect device to network: _switchMobileData ON ( or 1 ) +# o disconnect device: _switchMobileData OFF ( or 0 ) +# o get informations about the device: _getDeviceInformation and _getStatus and _getNetProvider +# all functions return XML formatted data in $response. +# o Check if device is connected: "if _isConnected; then .... fi" +# o $response can be parsed by calling _valueFromResponse +# e.g "_valueFromResponse msisdn" to get the phone number after a call to _getDeviceInformation +# +# +# Usage of functions +# - call the function with parameters (if required) +# - return code: 0 - success; 1 - failed +# - $status: status information (OK, ERROR) +# - $response: xml response to be parsed for the required information +# +# +# required software: curl, base64, sha256sum, sed +# +# +# zbchristian 2021 +# + +# Initialization procedure +# ======================== +# +# host=$host_default # ip address of device +# user="admin" # user name if locked (default admin) +# pw="1234Secret" # password if locked +# pin="1234" # PIN of SIM +# _initHilinkAPI # initialize the API +# +# Termination +# =========== +# cleanup the API before quitting the shell +# _closeHilinkAPI (optional: add parameter "save" to save the session/token data for subsequent calls. Valid for a few minutes.) + +host_default="192.168.8.1" +save_file="/tmp/hilink_api_saved.dat" +save_age=60 +header_file="/tmp/hilink_login_hdr.txt" + +# initialize +function _initHilinkAPI() { + if [ -z "$host" ]; then host=$host_default; fi + if ! _hostReachable; then return 1; fi + if [ -f $save_file ]; then # found file with saved data + _getSavedData + age=$(( $(date +%s) - $(stat $save_file -c %Y) )) + if [[ $age -gt $save_age ]]; then + rm -f $save_file + _logout + _sessToken + fi + fi + if [ -z "$sessID" ] || [ -z "$token" ]; then _sessToken; fi + _login + return $? +} + +function _getSavedData() { + if [ -f $save_file ]; then # restore saved session data + dat=$(cat $save_file) + sessID=$(echo "$dat" | sed -nr 's/sessionid: ([a-z0-9]*)/\1/ip') + token=$(echo "$dat" | sed -nr 's/token: ([a-z0-9]*)/\1/ip') + tokenlist=( $(echo "$dat" | sed -nr 's/tokenlist: ([a-z0-9 ]*)/\1/ip') ) + fi +} + +# Cleanup +# parameter: "save" - will store sessionid and tokens in file +function _closeHilinkAPI() { + if [ -z "$host" ]; then host=$host_default; fi + if ! _hostReachable; then return 1; fi + rm -f $save_file + [ ! -z "$1" ] && opt="${1,,}" + if [ ! -z "$opt" ] && [ "$opt" = "save" ]; then + echo "sessionid: $sessID" > $save_file + echo "token: $token" >> $save_file + echo "tokenlist: ${tokenlist[@]}" >> $save_file + fi + _logout + tokenlist="" + sessID="" + token="" + return 0 +} + +# get status (connection status, DNS, ) +# parameter: none +function _getStatus() { + if _login; then + if _sendRequest "api/monitoring/status"; then + if [ ! -z "$1" ]; then _valueFromResponse "$1"; fi + fi + return $? + fi + return 1 +} + +function _isConnected() { + conn=$(_getStatus "connectionstatus") + status="NO" + if [ ! -z "$conn" ] && [ $conn -eq 901 ]; then + status="YES" + return 0 + fi + return 1 +} + +# get device information (device name, imei, imsi, msisdn-phone number, MAC, WAN IP ...) +# parameter: name of parameter to return +function _getDeviceInformation() { + if _login; then + if _sendRequest "api/device/information"; then + if [ ! -z "$1" ]; then _valueFromResponse "$1"; fi + fi + return $? + fi + return 1 +} + +# get net provider information +# parameter: name of parameter to return +function _getNetProvider() { + if _login; then + if _sendRequest "api/net/current-plmn"; then + if [ ! -z "$1" ]; then _valueFromResponse "$1"; fi + fi + return $? + fi + return 1 +} + +# get signal level +# parameter: name of parameter to return +function _getSignal() { + if _login; then + if _sendRequest "api/device/signal"; then + if [ ! -z "$1" ]; then _valueFromResponse "$1"; fi + fi + return $? + fi + return 1 +} + +function _getAllInformations() { + if _getDeviceInformation; then _keyValuePairs; fi + if _getSignal; then _keyValuePairs; fi + if _getNetProvider; then _keyValuePairs; fi +} + +# get status of mobile data connection +# parameter: none +function _getMobileDataStatus() { + if _login; then + if _sendRequest "api/dialup/mobile-dataswitch"; then + status=$(_valueFromResponse "dataswitch") + if [ $? -eq 0 ] && [ ! -z "$status" ]; then echo "$status"; fi + fi + return $? + fi + return 1 +} + + +# PIN of SIM can be passed either as $pin, or as parameter +# parameter: PIN number of SIM card +function _enableSIM() { +#SimState: +#255 - no SIM, +#256 - error CPIN, +#257 - ready, +#258 - PIN disabled, +#259 - check PIN, +#260 - PIN required, +#261 - PUK required + if [ ! -z "$1" ]; then pin="$1"; fi + if ! _login; then return 1; fi + if _sendRequest "api/pin/status"; then + simstate=`echo $response | sed -rn 's/.*([0-9]*)<\/simstate>.*/\1/pi'` + if [[ $simstate -eq 257 ]]; then status="SIM ready"; return 0; fi + if [[ $simstate -eq 260 ]]; then + status="PIN required" + if [ ! -z "$pin" ]; then _setPIN "$pin"; fi + return $? + fi + if [[ $simstate -eq 255 ]]; then status="NO SIM"; return 1; fi + fi + return 1 +} + +# obtain session and verification token - stored in vars $sessID and $token +# parameter: none +function _sessToken() { + tokenlist="" + token="" + sessID="" + response=$(curl -s http://$host/api/webserver/SesTokInfo -m 5 2> /dev/null) + if [ -z "$response" ]; then echo "No access to device at $host"; return 1; fi + status=$(echo "$response" | sed -nr 's/.*([0-9]*)<\/code>.*/\1/ip') + if [ -z "$status" ]; then + token=`echo $response | sed -r 's/.*(.*)<\/TokInfo>.*/\1/'` + sessID=`echo $response | sed -r 's/.*(.*)<\/SesInfo>.*/\1/'` + if [ ! -z "$sessID" ] && [ ! -z "$token" ]; then + sessID="SessionID=$sessID" + return 0 + fi + fi + return 1 +} + +# unlock device (if locked) with user name and password +# requires stored user="admin"; pw="1234secret";host=$host_default +# parameter: none +function _login() { + if _loginState; then return 0; fi # login not required, or already done + _sessToken + # get password type + if ! _sendRequest "api/user/state-login"; then return 1; fi + pwtype=$(echo "$response" | sed -rn 's/.*([0-9])<\/password_type>.*/\1/pi') + if [ -z "$pwtype" ];then pwtype=4; fi # fallback is type 4 + if [[ ! -z "$user" ]] && [[ ! -z "$pw" ]]; then + # password encoding + # type 3 : base64(pw) encoded + # type 4 : base64(sha256sum(user + base64(sha256sum(pw)) + token)) + pwtype3=$(echo -n "$pw" | base64 --wrap=0) + hashedpw=$(echo -n "$pw" | sha256sum -b | sed -nr 's/^([0-9a-z]*).*$/\1/ip' ) + hashedpw=$(echo -n "$hashedpw" | base64 --wrap=0) + pwtype4=$(echo -n "$user$hashedpw$token" | sha256sum -b | sed -nr 's/^([0-9a-z]*).*$/\1/ip' ) + encpw=$(echo -n "$pwtype4" | base64 --wrap=0) + if [ $pwtype -ne 4 ]; then encpw=$pwtype3; fi + xmldata="$user$encpw$pwtype" + xtraopts="--dump-header $header_file" + rm -f $header_file + _sendRequest "api/user/login" + if [ ! -z "$status" ] && [ "$status" = "OK" ]; then + # store the list of 30 tokens. Each token is valid for a single request + tokenlist=( $(cat $header_file | sed -rn 's/^__RequestVerificationToken:\s*([0-9a-z#]*).*$/\1/pi' | sed 's/#/ /g') ) + _getToken + sessID=$(cat $header_file | grep -ioP 'SessionID=([a-z0-9]*)') + if [ ! -z "$sessID" ] && [ ! -z "$token" ]; then + return 0 + fi + fi + fi + return 1 +} + +# logout of hilink device +# parameter: none +function _logout() { + if _loginState; then + xmldata="1" + if _sendRequest "api/user/logout"; then + tokenlist="" + sessID="" + token="" + login_enabled="" + fi + return $? + fi + return 1 +} + +# parameter: none +function _loginState() { + status="OK" + if [ -z "$login_enabled" ]; then _checkLoginEnabled; fi + if [ $login_enabled -eq 1 ]; then return 0; fi # login is disabled + _sendRequest "api/user/state-login" + state=`echo "$response" | sed -rn 's/.*(.*)<\/state>.*/\1/pi'` + if [ ! -z "$state" ] && [ $state -eq 0 ]; then # already logged in + return 0 + fi + return 1 +} + +function _checkLoginEnabled() { + if _sendRequest "api/user/hilink_login"; then + login_enabled=0 + state=$(echo $response | sed -rn 's/.*(.*)<\/hilink_login>.*/\1/pi') + if [ ! -z "$state" ] && [ $state -eq 0 ]; then # no login enabled + login_enabled=1 + fi + else + login_enabled="" + fi +} + +# switch mobile data on/off 1/0 +# if SIM is locked, $pin has to be set +# parameter: state - ON/OFF or 1/0 +function _switchMobileData() { + if [ -z "$1" ]; then return 1; fi + _login + mode="${1,,}" + [ "$mode" = "on" ] && mode=1 + [ "$mode" = "off" ] && mode=0 + if [[ $mode -ge 0 ]]; then + if _enableSIM "$pin"; then + xmldata="$mode" + _sendRequest "api/dialup/mobile-dataswitch" + return $? + fi + fi + return 1 +} + +# parameter: PIN of SIM card +function _setPIN() { + if [ -z "$1" ]; then return 1; fi + pin="$1" + xmldata="0$pin" + _sendRequest "api/pin/operate" + return $? +} + +# Send request to host at http://$host/$apiurl +# data in $xmldata and options in $xtraopts +# parameter: apiurl (e.g. "api/user/login") +function _sendRequest() { + status="ERROR" + if [ -z "$1" ]; then return 1; fi + apiurl="$1" + ret=1 + if [ -z "$sessID" ] || [ -z "$token" ]; then _sessToken; fi + if [ -z "$xmldata" ];then + response=$(curl -s http://$host/$apiurl -m 10 \ + -H "Cookie: $sessID") + else + response=$(curl -s -X POST http://$host/$apiurl -m 10 \ + -H "Content-Type: text/xml" \ + -H "Cookie: $sessID" \ + -H "__RequestVerificationToken: $token" \ + -d "$xmldata" $xtraopts 2> /dev/null) + _getToken + fi + if [ ! -z "$response" ];then + response=$(echo $response | tr -d '\012\015') # delete newline chars + status=$(echo "$response" | sed -nr 's/.*([0-9]*)<\/code>.*/\1/ip') # check for error code + if [ -z "$status" ]; then + status="OK" + response=$(echo "$response" | sed -nr 's/.*(.*)<\/response>.*/\1/ip') + [ -z "$response" ] && response="none" + ret=0 + else + status="ERROR $status" + fi + else + status="ERROR" + fi + if [[ "$status" =~ ERROR ]]; then _handleError; fi + xtraopts="" + xmldata="" + return $ret +} + +# handle the list of tokens available after login +# parameter: none +function _getToken() { + if [ ! -z "$tokenlist" ] && [ ${#tokenlist[@]} -gt 0 ]; then + token=${tokenlist[0]} # get first token in list + tokenlist=("${tokenlist[@]:1}") # remove used token from list + if [ ${#tokenlist[@]} -eq 0 ]; then + _logout # use the last token to logout + fi + else + _sessToken # old token has been used - need new session + fi +} + +# Analyse $status for error code +# return error text in $status +function _handleError() { + txt=$(_getErrorText) + if [ -z "$code" ]; then return 1; fi + ret=0 + case "$code" in + 101|108003|108007) + ret=1 + status="$txt" + ;; + 108001|108002|108006) + ret=1 + status="$txt" + ;; + 125001|125002|125003) + _sessToken + ret=0 + ;; + *) + ;; + esac + return "$ret" +} + +declare -A err_hilink_api +err_hilink_api[101]="Unable to get session ID/token" +err_hilink_api[108001]="Invalid username/password" +err_hilink_api[108002]=${errors[108001]} +err_hilink_api[108006]=${errors[108001]} +err_hilink_api[108003]="User already logged in - need to wait a bit" +err_hilink_api[108007]="Too many login attempts - need to wait a bit" +err_hilink_api[125001]="Invalid session/request token" +err_hilink_api[125002]=${errors[125001]} +err_hilink_api[125003]=${errors[125001]} + +# check error and return error text +# status passsed in $status, or $1 +function _getErrorText() { + err="$status" + code="0" + if [ ! -z "$1" ]; then err="$1"; fi + if [ -z "$err" ]; then return 1; fi + errortext="$err" + if [[ "$err" =~ ERROR\ *([0-9]*) ]] && [ ! -z "${BASH_REMATCH[1]}" ]; then + code=${BASH_REMATCH[1]} + if [ ! -z "$code" ] && [ ! -z "${err_hilink_api[$code]}" ]; then + errortext="${err_hilink_api[$code]}" + fi + fi + echo $errortext + return 0 +} + +function _hostReachable() { + avail=`timeout 0.5 ping -c 1 $host | sed -rn 's/.*time=.*/1/p'` + if [ -z "$avail" ]; then return 1; fi + return 0; +} + +# helper function to parse $response (xml format) for a value +# call another function first! +# parameter: tag-name +function _valueFromResponse() { + if [ -z "$response" ] || [ -z "$1" ]; then return 1; fi + par="$1" + value=$(echo $response | sed -rn 's/.*<'$par'>(.*)<\/'$par'>.*/\1/pi') + if [ -z "$value" ]; then return 1; fi + echo "$value" + return 0 +} + +# list all keys of the current xml response +function _keysFromResponse() { + if [ -z "$response" ]; then return 1; fi + echo $response | grep -oiP "(?<=<)[a-z_-]*(?=>)" + return 0 +} + +# return all key=value pairs of the current xml response +function _keyValuePairs() { + if [ -z "$response" ]; then return 1; fi + echo $response | sed -n 's/<\([^>]*\)>\(.*\)<\/\1>[^<]*/\1=\"\2\"\n/gpi' + return 0 +} + +host=$host_default +user="admin" +pw="" +token="" +tokenlist="" +sessID="" +xmldata="" +xtraopts="" +response="" +status="" +pwtype=-1 + diff --git a/config/client_config/info_huawei.sh b/config/client_config/info_huawei.sh index bcdf8e379..475767fe0 100644 --- a/config/client_config/info_huawei.sh +++ b/config/client_config/info_huawei.sh @@ -12,43 +12,43 @@ # zbchristian 2020 # opt="device" -if [ ! -z $1 ]; then opt=${1,,}; fi +if [ ! -z "$1" ]; then opt=${1,,}; fi type="hilink" -if [ ! -z $2 ]; then type=${2,,}; fi +if [ ! -z "$2" ]; then type=${2,,}; fi -path=`dirname $0` -if [[ $type == "hilink" ]]; then +path=$(dirname "$0") +if [ "$type" = "hilink" ]; then connect="192.168.8.1" - if [ ! -z $3 ]; then connect=$3; fi + if [ ! -z "$3" ]; then connect=$3; fi script="$path/info_huawei_hilink.sh" else connect="/dev/ttyUSB2" - if [ ! -z $3 ]; then connect=$3; fi + if [ ! -z "$3" ]; then connect=$3; fi script="$path/info_huawei_modem.sh" fi -res=`$script $opt $connect` +res=$($script $opt $connect) # some results require special treatment case $opt in # manufacturer) -# if [[ $res == "none" ]]; then res="Huawei"; fi +# if [ "$res" = "none" ]; then res="Huawei"; fi # ;; # device) -# if [[ ! $res == "none" ]]; then res="Huawei $res"; +# if [ ! "$res" = "none" ]; then res="Huawei $res"; # else res="Huawei"; fi # ;; mode) - if [[ ! $res == "none" ]]; then - if [[ $type == "hilink" ]]; then - if [[ $res == "LTE" ]]; then res="4G" - elif [[ $res == "WCDMA" ]]; then res="3G"; + if [ ! "$res" = "none" ]; then + if [ "$type" = "hilink" ]; then + if [ "$res" = "LTE" ]; then res="4G" + elif [ "$res" = "WCDMA" ]]; then res="3G"; else res="2G"; fi else - if [[ $res == 7 ]]; then res="4G" - elif [[ $res < 7 ]] && [[ $res > 2 ]] ; then res="3G"; + if [ $res -eq 7 ]; then res="4G" + elif [ $res -lt 7 ] && [ $res -gt 2 ] ; then res="3G"; else res="2G"; fi fi fi @@ -56,45 +56,45 @@ case $opt in signal) # return signal strength/quality in % - if [[ $type == "hilink" ]]; then + if [ "$type" = "hilink" ]; then # signal request tries to get RSRQ value # try to get RSRQ (4G), EC/IO (3G) or RSSI (2G) value - if [[ $res == "none" ]]; then res=`$script "ecio"`; fi - if [[ ! $res == "none" ]]; then + if [ "$res" = "none" ]; then res=$($script "ecio"); fi + if [ ! "$res" = "none" ]; then # for rsrq and ecio assume: -3dB (100%) downto -20dB (0%) qual=${res//dB/} if [[ ! "$qual" =~ [-0-9\.]* ]]; then qual=-100; fi qual=$(bc <<< "scale=0;res=$qual-0.5;res/1") # just round to next integer - if [[ $qual -le -20 ]]; then qual=0; - elif [[ $qual -ge -3 ]]; then qual=100; + if [ $qual -le -20 ]; then qual=0; + elif [ $qual -ge -3 ]; then qual=100; else qual=$(bc <<< "scale=0;res=100.0/17.0*$qual+2000.0/17.0;res/1"); fi else # try rssi: >-70dBm (100%) downto -100dBm (0%) - res=`$script "rssi"`; - if [[ ! $res == "none" ]]; then - if [[ $res =~ [-0-9\.]* ]]; then res="-120 dBm"; fi + res=$($script "rssi"); + if [ ! "$res" = "none" ]; then + if [[ ! $res =~ [-0-9\.]* ]]; then res="-120 dBm"; fi qual=${res//dBm/} qual=$(bc <<< "scale=0;res=$qual+0.5;res/1") # just round to next integer - if [[ $qual -le -110 ]]; then qual=0; - elif [[ $qual -ge -70 ]]; then qual=100; + if [ $qual -le -110 ]; then qual=0; + elif [ $qual -ge -70 ]; then qual=100; else qual=$(bc <<< "scale=0;res=2.5*$qual+275;res/1"); fi fi fi else # modem returns RSSI as number 0-31 - 0 = -113dB (0%), 1 = -111dB, 31 = >=51dB (100%) qual=$(bc <<< "scale=0;res=$res*3.5+0.5;res/1") - if [[ $qual -gt 100 ]]; then res=100; fi + if [ $qual -gt 100 ]; then res=100; fi fi - if [[ ! "$res" == "none" ]]; then res="$res (${qual}%)"; fi + if [ ! "$res" = "none" ]; then res="$res (${qual}%)"; fi ;; - + operator) # check if operator/network is just a 5 digit number -> extract network name from table if [[ $res =~ ^[0-9]{5}$ ]]; then mcc=${res:0:3} mnc=${res:3:2} op=$(cat $path/mcc-mnc-table.csv | sed -rn 's/^'$mcc'\,[0-9]*\,'$mnc'\,(.*\,){4}(.*)$/\2/p') - if [[ ! -z $op ]]; then res="$op ($res)"; fi + if [ ! -z "$op" ]; then res="$op ($res)"; fi fi ;; diff --git a/config/client_config/info_huawei_hilink.sh b/config/client_config/info_huawei_hilink.sh index 0bae3ecfb..d3b20541c 100644 --- a/config/client_config/info_huawei_hilink.sh +++ b/config/client_config/info_huawei_hilink.sh @@ -1,49 +1,75 @@ #!/bin/bash -# Infosramtion about HUAWEI hilink (router) modem -# ----------------------------------------------- +# Information about HUAWEI hilink (router) modem +# ---------------------------------------------- # get info about the device and signal # parameter: $1 - see opts list below # $2 - host ip address for API calls (optional) # returns the value of the parameter, or "none" if not found or empty # +# All device informations are buffered for 5 secs to speed up subsequent calls +# # zbchristian 2020 -opts=("device" "imei" "imsi" "telnumber" "ipaddress" "mode" "signal" "rssi" "rsrq" "rsrp" "sinr" "ecio" "operator") - -# xml tags to extract information from -tags=("devicename" "imei" "imsi" "msisdn" "wanipaddress" "workmode" "rsrq" "rssi" "rsrq" "rsrp" "sinr" "ecio" "fullname") -iurl=( 0 0 0 0 0 0 1 1 1 1 1 1 2) -# api urls -urls=("api/device/information" "api/device/signal" "api/net/current-plmn") +if [ -z "$1" ]; then echo "none"; exit; fi host="192.168.8.1" -if [ ! -z $2 ]; then host=$2; fi - -avail=`timeout 0.5 ping -c 1 $host | sed -rn 's/.*time=.*/1/p'` -if [[ -z $avail ]]; then echo "none"; exit; fi - -idx=-1 -opt=${opts[0]} -if [ ! -z $1 ]; then opt=$1; fi - -for i in "${!opts[@]}"; do - if [[ ${opts[$i]} == $opt ]]; then idx=$i; fi -done -if [[ $idx == -1 ]];then echo "none"; exit; fi - -par=${tags[$idx]} -iu=${iurl[$idx]} - -url="http://$host/${urls[$iu]}" -# echo "Found option $opt at index $idx - tag $par url $url " - -info="" -if [ ! -z $url ]; then info=`curl -s $url`; fi +status="no option given" +if [ ! -z "$2" ]; then host="$2"; fi -result=`echo $info | sed -rn 's/.*<'"$par"'>(.*)<\/'"$par"'>.*/\1/pi'` +opt="${1,,}" +result="none" +if [ "$opt" = "connected" ]; then + source /usr/local/sbin/huawei_hilink_api.sh + if ! _initHilinkAPI; then echo "none"; exit; fi + result=$(_getMobileDataStatus) + _closeHilinkAPI +else + info_file="/tmp/huawei_infos_$host.dat" + if [ -f "$info_file" ]; then + age=$(( $(date +%s) - $(stat $info_file -c %Y) )) + if [[ $age -gt 5 ]]; then rm -f $info_file; fi + fi -if [ -z "$result" ]; then result="none"; fi + if [ -f "$info_file" ]; then + infos=$(cat $info_file) + else + source /usr/local/sbin/huawei_hilink_api.sh + if ! _initHilinkAPI; then echo "none"; exit; fi + infos=$(_getAllInformations) + _closeHilinkAPI + if [ ! -z "$infos" ]; then echo "$infos" > /tmp/huawei_infos_$host.dat; fi + fi -echo $result + case "$opt" in + device|devicename) + key="devicename" + ;; + ipaddress|wanipaddress) + key="wanipaddress" + ;; + mode) + key="workmode" + ;; + telnumber) + key="msisdn" + ;; + imei|imsi|rssi|rsrq|rsrp|sinr|ecio) + key="$opt" + ;; + signal) + key="rsrq" + ;; + operator|fullname) + key="fullname" + ;; + *) + key="" + ;; + esac + if [ -z "$key" ]; then result="none"; fi + result=$(echo "$infos" | sed -rn 's/'$key'=\"([^ \s]*)\"/\1/ip') + if [ -z "$result" ]; then result="none"; fi +fi +echo -n "$result" diff --git a/config/client_config/onoff_huawei_hilink.sh b/config/client_config/onoff_huawei_hilink.sh index 18b1a016b..65587d01d 100644 --- a/config/client_config/onoff_huawei_hilink.sh +++ b/config/client_config/onoff_huawei_hilink.sh @@ -8,89 +8,12 @@ # -h 192.168.8.1 - host ip address # -p 1234 - PIN of SIM card # -c 0/1 - connect - set datamode off/on -# required software: curl, base64 +# required software: curl, base64, sha256sum # -# TODO: implement login into API - currently the login has to be disabled! -# -# zbchristian 2020 - -# obtain session and verification token -function _SessToken() { - SesTok=`sudo curl -s http://$host/api/webserver/SesTokInfo -m 5 2> /dev/null` - if [ -z "$SesTok" ]; then exit; fi - - token=`echo $SesTok | sed -r 's/.*(.*)<\/TokInfo>.*/\1/'` - sesinfo=`echo $SesTok | sed -r 's/.*(.*)<\/SesInfo>.*/\1/'` -} - -function _login() { -# ----------------------- THIS DOES NOT WORK ------------------------------------------ -# login to web api - _SessToken - - if [[ ! -z $user ]] && [[ ! -z $pw ]]; then - # password encoding - # type 3 : base64(pw) encoded - # type 4 : base64(sha256sum(user + base64(sha256sum(pw)) + token)) - pwtype3=$(echo $pw | base64 --wrap=0) - hashedpw=$(echo -n "$pw" | sha256sum -b | cut -d " " -f1 | base64 --wrap=0) - pwtype4=$(echo -n "$user$hashedpw$token" | sha256sum -b | cut -d " " -f1 | base64 --wrap=0) - apiurl="api/user/login" - xmldata="$user$pwtype44" -# xmldata="$user$pwtype33" - xtraopts="--dump-header /tmp/hilink_login_hdr.txt" - _sendRequest - # get updated session cookie - sesinfo=$(grep "SessionID=" /tmp/hilink_login_hdr.txt | cut -d ':' -f2 | cut -d ';' -f1) - token=$(grep "__RequestVerificationTokenone" /tmp/hilink_login_hdr.txt | cut -d ':' -f2) -echo "Login Cookie $sesinfo" -echo "Login Token $token" - fi -# ------------------------------ DO NOT USE THE LOGIN CODE ---------------------------------- -} +# zbchristian 2021 -function _switchMobileData() { -# switch mobile data on/off - if [[ $datamode -ge 0 ]]; then - xmldata="$datamode" - apiurl="api/dialup/mobile-dataswitch" - _sendRequest - fi -} - -function _enableSIM() { -#SimState: -#255 - no SIM, -#256 - error CPIN, -#257 - ready, -#258 - PIN disabled, -#259 - check PIN, -#260 - PIN required, -#261 - PUK required - status=`curl -s http://$host/api/pin/status -m 10` - state=`echo $status | sed -rn 's/.*(.*)<\/simstate>.*/\1/pi'` - if [[ $state -eq 257 ]]; then echo "Hilink: SIM ready"|systemd-cat; return; fi - if [[ $state -eq 260 ]]; then echo "Hilink: Set PIN"|systemd-cat; _setPIN; fi -} - -function _setPIN() { - if [[ ! -z $pin ]]; then - xmldata="0$pin" - apiurl="api/pin/operate" - _sendRequest - fi -} - -function _sendRequest() { - result="" - if [[ -z $xmldata ]]; then return; fi - result=`curl -s http://$host/$apiurl -m 10 \ - -H "Content-Type: application/xml" \ - -H "Cookie: $sesinfo" \ - -H "__RequestVerificationToken: $token" \ - -d "$xmldata" $xtraopts 2> /dev/null` - xtraopts="" -} +# include the hilink API +source /usr/local/sbin/huawei_hilink_api.sh # handle options @@ -98,8 +21,8 @@ host="192.168.8.1" pin="" user="" pw="" -datamode=-1 -connect=-1 +datamode="" + while getopts ":c:h:l:m:p:" opt; do case $opt in h) if [[ $OPTARG =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then host="$OPTARG"; fi @@ -109,7 +32,7 @@ while getopts ":c:h:l:m:p:" opt; do l) if [[ $OPTARG =~ ^[0-9a-zA-Z]*:.*$ ]]; then user=$(echo "$OPTARG" | cut -d':' -f1); pw=$(echo "$OPTARG" | cut -d':' -f2); - fi + fi ;; c) if [[ $OPTARG == "1" ]]; then datamode=1; else datamode=0; fi ;; @@ -118,21 +41,11 @@ done echo "Hilink: switch device at $host to mode $datamode" | systemd-cat -# check if device is reachable -avail=`timeout 0.5 ping -c 1 $host | sed -rn 's/.*time=.*/1/p'` -if [[ -z $avail ]]; then - echo "Hilink: no link to host" | systemd-cat - exit -fi +status="usage: -c 1/0 to disconnect/disconnect" +if [ -z "$datamode" ] || [ ! _initHilinkAPI ]; then echo "Hilink: failed - return status: $status"; exit; fi -token="" -Sesinfo="" -xmldata="" -xtraopts="" -result="" +if ! _switchMobileData "$datamode"; then echo "Hilink: could not switch the data mode on/off . Error: ";_getErrorText; fi -_SessToken -_enableSIM -_switchMobileData # check and perform enable/disable mobile data connection +if ! _closeHilinkAPI; then echo -n "Hilink: failed - return status: $status . Error: ";_getErrorText; fi From a58845ed9eb7a6585d5b132de7fb6b5ef3dbcc2d Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Thu, 20 May 2021 09:37:20 +0200 Subject: [PATCH 185/300] Update common.sh --- installers/common.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/installers/common.sh b/installers/common.sh index e3733427b..4988c1130 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -583,12 +583,10 @@ function _configure_networking() { # Add sudoers file to /etc/sudoers.d/ and set file permissions function _patch_system_files() { - # Create sudoers if not present - if [ ! -f $raspap_sudoers ]; then - _install_log "Adding raspap.sudoers to ${raspap_sudoers}" - sudo cp "$webroot_dir/installers/raspap.sudoers" $raspap_sudoers || _install_status 1 "Unable to apply raspap.sudoers to $raspap_sudoers" - sudo chmod 0440 $raspap_sudoers || _install_status 1 "Unable to change file permissions for $raspap_sudoers" - fi + # Create sudoers + _install_log "Adding raspap.sudoers to ${raspap_sudoers}" + sudo cp "$webroot_dir/installers/raspap.sudoers" $raspap_sudoers || _install_status 1 "Unable to apply raspap.sudoers to $raspap_sudoers" + sudo chmod 0440 $raspap_sudoers || _install_status 1 "Unable to change file permissions for $raspap_sudoers" # Add symlink to prevent wpa_cli cmds from breaking with multiple wlan interfaces _install_log "Symlinked wpa_supplicant hooks for multiple wlan interfaces" From b99752c4cd9931fe64d42d705068b796d1553900 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Fri, 21 May 2021 14:57:14 +0200 Subject: [PATCH 186/300] Implement login for Hilink devices --- ajax/networking/save_net_dev_config.php | 28 ++-- config/client_config/info_huawei.sh | 11 +- config/client_config/info_huawei_hilink.sh | 134 +++++++++++--------- config/client_config/onoff_huawei_hilink.sh | 46 +++---- config/config.php | 1 + includes/get_clients.php | 31 +++-- templates/networking.php | 2 +- 7 files changed, 131 insertions(+), 122 deletions(-) diff --git a/ajax/networking/save_net_dev_config.php b/ajax/networking/save_net_dev_config.php index be2c04dd0..a95baa50a 100644 --- a/ajax/networking/save_net_dev_config.php +++ b/ajax/networking/save_net_dev_config.php @@ -14,19 +14,26 @@ if (isset($_POST['interface'])) { $int = $_POST['interface']; $cfg = []; - $file = $int.".ini"; + $file = $RASPI_MOBILEDATA_CONFIG; $cfgfile="/etc/wvdial.conf"; if ( $int == "mobiledata") { $cfg['pin'] = $_POST["pin-mobile"]; $cfg['apn'] = $_POST["apn-mobile"]; $cfg['apn_user'] = $_POST["apn-user-mobile"]; $cfg['apn_pw'] = $_POST["apn-pw-mobile"]; + $cfg['router_user'] = $cfg['apn_user'] ; + $cfg['router_pw'] = $cfg['apn_pw'] ; if (file_exists($cfgfile)) { if($cfg["pin"] !== "") exec('sudo /bin/sed -i "s/CPIN=\".*\"/CPIN=\"'.$cfg["pin"].'\"/gi" '.$cfgfile); if($cfg["apn"] !== "") exec('sudo /bin/sed -i "s/\"IP\"\,\".*\"/\"IP\"\,\"'.$cfg["apn"].'\"/gi" '.$cfgfile); if($cfg["apn_user"] !== "") exec('sudo /bin/sed -i "s/^username = .*$/Username = '.$cfg["apn_user"].'/gi" '.$cfgfile); if($cfg["apn_pw"] !== "") exec('sudo /bin/sed -i "s/^password = .*$/Password = '.$cfg["apn_pw"].'/gi" '.$cfgfile); } + if (write_php_ini($cfg, RASPI_MOBILEDATA_CONFIG)) { + $jsonData = ['return'=>0,'output'=>['Successfully saved mobile data settings']]; + } else { + $jsonData = ['return'=>1,'output'=>['Error saving mobile data settings']]; + } } else if ( preg_match("/netdevices/",$int)) { if(!isset($_POST['opts']) ) { $jsonData = ['return'=>0,'output'=>['No valid data to add/delete udev rule ']]; @@ -75,26 +82,9 @@ if (!empty($rule)) exec('echo \''.$rule.'\' | sudo /usr/bin/tee -a '.$udevfile); } $jsonData = ['return'=>0,'output'=>['Settings changed for device '.$dev. '
Changes will only be in effect after reconnecting the device' ] ]; - echo json_encode($jsonData); - return; } } else { - $ip = $_POST[$int.'-ipaddress']; - $netmask = mask2cidr($_POST[$int.'-netmask']); - $dns1 = $_POST[$int.'-dnssvr']; - $dns2 = $_POST[$int.'-dnssvralt']; - - $cfg['interface'] = $int; - $cfg['routers'] = $_POST[$int.'-gateway']; - $cfg['ip_address'] = $ip."/".$netmask; - $cfg['domain_name_server'] = $dns1." ".$dns2; - $cfg['static'] = $_POST[$int.'-static']; - $cfg['failover'] = $_POST[$int.'-failover']; - } - if (write_php_ini($cfg, RASPI_CONFIG.'/networking/'.$file)) { - $jsonData = ['return'=>0,'output'=>['Successfully Updated Network Configuration']]; - } else { - $jsonData = ['return'=>1,'output'=>['Error saving network configuration to file']]; + $jsonData = ['return'=>1,'output'=>['Unknown network configuration']]; } } else { $jsonData = ['return'=>2,'output'=>'Unable to detect interface']; diff --git a/config/client_config/info_huawei.sh b/config/client_config/info_huawei.sh index 475767fe0..540ede98c 100644 --- a/config/client_config/info_huawei.sh +++ b/config/client_config/info_huawei.sh @@ -5,28 +5,31 @@ # $2 : (optional) type - hilink or modem (default: hilink) # $3 : (optional) for hilink: ip address of the device (default: 192.168.8.1) # for modem: tty interface for communication (default: /dev/ttypUSB2) +# $4 : more options can be added for Hilink devices ('-u user -P password -p pin'). These are passed to the corresponding script # # requires: bc # calls the scripts info_huawei_hilink.sh and info_huawei_modem.sh (same path as this script) # # zbchristian 2020 # +path=$(dirname "$0") opt="device" if [ ! -z "$1" ]; then opt=${1,,}; fi type="hilink" if [ ! -z "$2" ]; then type=${2,,}; fi -path=$(dirname "$0") +parms="" if [ "$type" = "hilink" ]; then - connect="192.168.8.1" - if [ ! -z "$3" ]; then connect=$3; fi + connect="-h 192.168.8.1" + if [ ! -z "$3" ]; then connect="-h $3"; fi + if [ ! -z "$4" ]; then parms="$4"; fi script="$path/info_huawei_hilink.sh" else connect="/dev/ttyUSB2" if [ ! -z "$3" ]; then connect=$3; fi script="$path/info_huawei_modem.sh" fi -res=$($script $opt $connect) +res=$($script $opt $connect $parms) # some results require special treatment case $opt in diff --git a/config/client_config/info_huawei_hilink.sh b/config/client_config/info_huawei_hilink.sh index d3b20541c..345c1fe50 100644 --- a/config/client_config/info_huawei_hilink.sh +++ b/config/client_config/info_huawei_hilink.sh @@ -1,75 +1,93 @@ #!/bin/bash -# Information about HUAWEI hilink (router) modem -# ---------------------------------------------- +# Information about HUAWEI hilink +# ------------------------------- # get info about the device and signal -# parameter: $1 - see opts list below -# $2 - host ip address for API calls (optional) +# parameter: $1 - "connected", "device", "ipaddress", "mode", "signal" (see case statement below) +# -u,--user - username +# -P,--password - password +# -p,--pin - SIM pin +# -h,--host - host ip address for API calls (optional) # returns the value of the parameter, or "none" if not found or empty # # All device informations are buffered for 5 secs to speed up subsequent calls # -# zbchristian 2020 +# zbchristian 2021 -if [ -z "$1" ]; then echo "none"; exit; fi - -host="192.168.8.1" - -status="no option given" -if [ ! -z "$2" ]; then host="$2"; fi +function _setAPIParams() { + if [ ! -z "$hostip" ]; then host="$hostip"; fi + if [ ! -z "$username" ]; then user="$username"; fi + if [ ! -z "$password" ]; then pw="$password"; fi + if [ ! -z "$simpin" ]; then pin="$simpin"; fi +} +if [ -z "$1" ]; then echo "none"; exit; fi opt="${1,,}" +shift +while [ -n "$1" ]; do + case "$1" in + -u|--user) username="$2"; shift ;; + -P|--password) password="$2"; shift ;; + -p|--pin) simpin="$2"; shift ;; + -h|--host) hostip="$2"; shift ;; + esac + shift +done + +status="no valid option given" result="none" if [ "$opt" = "connected" ]; then - source /usr/local/sbin/huawei_hilink_api.sh - if ! _initHilinkAPI; then echo "none"; exit; fi - result=$(_getMobileDataStatus) - _closeHilinkAPI + source /usr/local/sbin/huawei_hilink_api.sh + if ! _initHilinkAPI; then echo "none"; exit; fi + _setAPIParams + result=$(_getMobileDataStatus) + _closeHilinkAPI else - info_file="/tmp/huawei_infos_$host.dat" - if [ -f "$info_file" ]; then - age=$(( $(date +%s) - $(stat $info_file -c %Y) )) - if [[ $age -gt 5 ]]; then rm -f $info_file; fi - fi + info_file="/tmp/huawei_infos_$host.dat" + if [ -f "$info_file" ]; then + age=$(( $(date +%s) - $(stat $info_file -c %Y) )) + if [[ $age -gt 5 ]]; then rm -f $info_file; fi + fi - if [ -f "$info_file" ]; then - infos=$(cat $info_file) - else - source /usr/local/sbin/huawei_hilink_api.sh - if ! _initHilinkAPI; then echo "none"; exit; fi - infos=$(_getAllInformations) - _closeHilinkAPI - if [ ! -z "$infos" ]; then echo "$infos" > /tmp/huawei_infos_$host.dat; fi - fi + if [ -f "$info_file" ]; then + infos=$(cat $info_file) + else + source /usr/local/sbin/huawei_hilink_api.sh + if ! _initHilinkAPI; then echo "none"; exit; fi + _setAPIParams + infos=$(_getAllInformations) + _closeHilinkAPI + if [ ! -z "$infos" ]; then echo "$infos" > /tmp/huawei_infos_$host.dat; fi + fi - case "$opt" in - device|devicename) - key="devicename" - ;; - ipaddress|wanipaddress) - key="wanipaddress" - ;; - mode) - key="workmode" - ;; - telnumber) - key="msisdn" - ;; - imei|imsi|rssi|rsrq|rsrp|sinr|ecio) - key="$opt" - ;; - signal) - key="rsrq" - ;; - operator|fullname) - key="fullname" - ;; - *) - key="" - ;; - esac - if [ -z "$key" ]; then result="none"; fi - result=$(echo "$infos" | sed -rn 's/'$key'=\"([^ \s]*)\"/\1/ip') - if [ -z "$result" ]; then result="none"; fi + case "$opt" in + device|devicename) + key="devicename" + ;; + ipaddress|wanipaddress) + key="wanipaddress" + ;; + mode) + key="workmode" + ;; + telnumber) + key="msisdn" + ;; + imei|imsi|rssi|rsrq|rsrp|sinr|ecio) + key="$opt" + ;; + signal) + key="rsrq" + ;; + operator|fullname) + key="fullname" + ;; + *) + key="" + ;; + esac + if [ -z "$key" ]; then result="none"; fi + result=$(echo "$infos" | sed -rn 's/'$key'=\"([^ \s]*)\"/\1/ip') + if [ -z "$result" ]; then result="none"; fi fi echo -n "$result" diff --git a/config/client_config/onoff_huawei_hilink.sh b/config/client_config/onoff_huawei_hilink.sh index 65587d01d..2ee413d25 100644 --- a/config/client_config/onoff_huawei_hilink.sh +++ b/config/client_config/onoff_huawei_hilink.sh @@ -1,42 +1,30 @@ #!/bin/bash # connect/disconnect Huawei mobile data stick in Hilink mode (e.g. E3372h) # ======================================================================== -# - send xml formatted string via HTTP API to stick -# - Requires session and verification token, which is obtained by an API call # -# options: -l "user":"password" - login data - DOES NOT WORK YET -# -h 192.168.8.1 - host ip address -# -p 1234 - PIN of SIM card -# -c 0/1 - connect - set datamode off/on +# options: -u, --user - user name (default "admin") +# -P, --password - password +# -h, --host - host ip address (default 192.168.8.1) +# -p, --pin - PIN of SIM card +# -c, --connect - connect 0/1 to set datamode off/on +# # required software: curl, base64, sha256sum # # zbchristian 2021 -# include the hilink API +# include the hilink API (defaults: user=admin, host=192.168.8.1) source /usr/local/sbin/huawei_hilink_api.sh -# handle options - -host="192.168.8.1" -pin="" -user="" -pw="" datamode="" - -while getopts ":c:h:l:m:p:" opt; do - case $opt in - h) if [[ $OPTARG =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then host="$OPTARG"; fi - ;; - p) if [[ $OPTARG =~ ^[0-9]{4,8} ]]; then pin="$OPTARG"; fi - ;; - l) if [[ $OPTARG =~ ^[0-9a-zA-Z]*:.*$ ]]; then - user=$(echo "$OPTARG" | cut -d':' -f1); - pw=$(echo "$OPTARG" | cut -d':' -f2); - fi - ;; - c) if [[ $OPTARG == "1" ]]; then datamode=1; else datamode=0; fi - ;; - esac +while [ -n "$1" ]; do + case "$1" in + -u|--user) user="$2"; shift ;; + -P|--password) pw="$2"; shift ;; + -p|--pin) if [[ $2 =~ ^[0-9]{4,8} ]]; then pin="$2"; fi; shift ;; + -h|--host) host="$2"; shift ;; + -c|--connect) if [ "$2" = "1" ]; then datamode=1; else datamode=0; fi; shift ;; + esac + shift done echo "Hilink: switch device at $host to mode $datamode" | systemd-cat @@ -44,7 +32,7 @@ echo "Hilink: switch device at $host to mode $datamode" | systemd-cat status="usage: -c 1/0 to disconnect/disconnect" if [ -z "$datamode" ] || [ ! _initHilinkAPI ]; then echo "Hilink: failed - return status: $status"; exit; fi -if ! _switchMobileData "$datamode"; then echo "Hilink: could not switch the data mode on/off . Error: ";_getErrorText; fi +if ! _switchMobileData "$datamode"; then echo -n "Hilink: could not switch the data mode on/off . Error: ";_getErrorText; fi if ! _closeHilinkAPI; then echo -n "Hilink: failed - return status: $status . Error: ";_getErrorText; fi diff --git a/config/config.php b/config/config.php index 8003a3206..87b178305 100755 --- a/config/config.php +++ b/config/config.php @@ -28,6 +28,7 @@ define('RASPI_ACCESS_CHECK_IP', '1.1.1.1'); define('RASPI_ACCESS_CHECK_DNS', 'one.one.one.one'); define('RASPI_CLIENT_CONFIG_PATH', '/etc/raspap/networking/client_udev_prototypes.json'); +define('RASPI_MOBILEDATA_CONFIG', '/etc/raspap/networking/mobiledata.ini'); define('RASPI_CLIENT_SCRIPT_PATH', '/usr/local/sbin'); // Constant for the 5GHz wireless regulatory domain diff --git a/includes/get_clients.php b/includes/get_clients.php index 094cb8bf9..11a74e943 100644 --- a/includes/get_clients.php +++ b/includes/get_clients.php @@ -128,23 +128,26 @@ function getClients($simple=true) $cl["device"][$i]["operator"] = $res[0]; break; case "hilink": + $pin=$user=$pw=""; + getMobileLogin($pin,$pw,$user); + $opts=$pin.' '.$user.' '.$pw; unset($res); // exec("ip link show $dev 2> /dev/null | grep -oP ' UP '",$res); exec("ifconfig -a | grep -i $dev -A 1 | grep -oP '(?<=inet )([0-9]{1,3}\.){3}'", $apiadd); $apiadd = !empty($apiadd) ? $apiadd[0]."1" : ""; unset($res); - exec("$path/info_huawei.sh mode hilink $apiadd", $res); + exec("$path/info_huawei.sh mode hilink $apiadd \"$opts\" ", $res); $cl["device"][$i]["mode"] = $res[0]; unset($res); - exec("$path/info_huawei.sh device hilink $apiadd", $res); + exec("$path/info_huawei.sh device hilink $apiadd \"$opts\" ", $res); if ($res[0] != "none" ) { $cl["device"][$i]["model"] = $res[0]; } unset($res); - exec("$path/info_huawei.sh signal hilink $apiadd", $res); + exec("$path/info_huawei.sh signal hilink $apiadd \"$opts\" ", $res); $cl["device"][$i]["signal"] = $res[0]; unset($ipadd); - exec("$path/info_huawei.sh ipaddress hilink $apiadd", $ipadd); + exec("$path/info_huawei.sh ipaddress hilink $apiadd \"$opts\" ", $ipadd); if (!empty($ipadd) && $ipadd[0] !== "none" ) { $cl["device"][$i]["connected"] = "y"; $cl["device"][$i]["wan_ip"] = $ipadd[0]; @@ -153,7 +156,7 @@ function getClients($simple=true) $cl["device"][$i]["wan_ip"] = "-"; } unset($res); - exec("$path/info_huawei.sh operator hilink $apiadd", $res); + exec("$path/info_huawei.sh operator hilink $apiadd \"$opts\" ", $res); $cl["device"][$i]["operator"] = $res[0]; break; case "phone": @@ -194,6 +197,15 @@ function getClientType($dev) { return $type; } +function getMobileLogin(&$pin,&$pw,&$user) { + if (file_exists(($f = RASPI_MOBILEDATA_CONFIG))) { + $dat = parse_ini_file($f); + $pin = (isset($dat["pin"]) && preg_match("/^[0-9]*$/", $dat["pin"])) ? "-p ".$dat["pin"] : ""; + $user = (isset($dat["router_user"]) && !empty($dat["router_user"]) ) ? "-u ".$dat["router_user"] : ""; + $pw = (isset($dat["router_pw"]) && !empty($dat["router_pw"]) ) ? "-P ".$dat["router_pw"] : ""; + } +} + function loadClientConfig() { // load network device config file for UDEV rules into $_SESSION @@ -271,12 +283,9 @@ function setClientState($state) preg_match("/^([0-9]{1,3}\.){3}/", $connected, $ipadd); $ipadd = $ipadd[0].'1'; // ip address of the Hilink api $mode = ($state == "up") ? 1 : 0; - $pin=""; - if (file_exists(($f = RASPI_CONFIG."/networking/mobiledata.ini"))) { - $dat = parse_ini_file($f); - $pin = (isset($dat["pin"]) && preg_match("/^[0-9]*$/", $dat["pin"])) ? $dat["pin"] : ""; - } - exec('sudo '.RASPI_CLIENT_SCRIPT_PATH.'/onoff_huawei_hilink.sh -c '.$mode.' -h '.$ipadd.' -p '.$pin); + $pin=$user=$pw=""; + getMobileLogin($pin,$pw,$user) + exec('sudo '.RASPI_CLIENT_SCRIPT_PATH.'/onoff_huawei_hilink.sh -c '.$mode.' -h '.$ipadd.' '.$pin.' '.$user.' '.$pw); break; case "ppp": if ($state == "up") { diff --git a/templates/networking.php b/templates/networking.php index f497cb289..578495e0e 100755 --- a/templates/networking.php +++ b/templates/networking.php @@ -81,7 +81,7 @@
-
From 26a50993b997160634dc8aee8ec341f19ce9d7df Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Fri, 21 May 2021 22:07:04 +0200 Subject: [PATCH 187/300] Fix hilink login --- config/client_config/huawei_hilink_api.sh | 8 +++---- config/client_config/info_huawei_hilink.sh | 26 ++++++++++++---------- includes/get_clients.php | 2 +- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/config/client_config/huawei_hilink_api.sh b/config/client_config/huawei_hilink_api.sh index c4402a6e7..5c9fd809b 100644 --- a/config/client_config/huawei_hilink_api.sh +++ b/config/client_config/huawei_hilink_api.sh @@ -232,6 +232,7 @@ function _login() { if ! _sendRequest "api/user/state-login"; then return 1; fi pwtype=$(echo "$response" | sed -rn 's/.*([0-9])<\/password_type>.*/\1/pi') if [ -z "$pwtype" ];then pwtype=4; fi # fallback is type 4 + ret=1 if [[ ! -z "$user" ]] && [[ ! -z "$pw" ]]; then # password encoding # type 3 : base64(pw) encoded @@ -251,12 +252,11 @@ function _login() { tokenlist=( $(cat $header_file | sed -rn 's/^__RequestVerificationToken:\s*([0-9a-z#]*).*$/\1/pi' | sed 's/#/ /g') ) _getToken sessID=$(cat $header_file | grep -ioP 'SessionID=([a-z0-9]*)') - if [ ! -z "$sessID" ] && [ ! -z "$token" ]; then - return 0 - fi + if [ ! -z "$sessID" ] && [ ! -z "$token" ]; then ret=0; fi fi + rm -f $header_file fi - return 1 + return $ret } # logout of hilink device diff --git a/config/client_config/info_huawei_hilink.sh b/config/client_config/info_huawei_hilink.sh index 345c1fe50..24145bef8 100644 --- a/config/client_config/info_huawei_hilink.sh +++ b/config/client_config/info_huawei_hilink.sh @@ -14,35 +14,37 @@ # zbchristian 2021 function _setAPIParams() { - if [ ! -z "$hostip" ]; then host="$hostip"; fi - if [ ! -z "$username" ]; then user="$username"; fi - if [ ! -z "$password" ]; then pw="$password"; fi - if [ ! -z "$simpin" ]; then pin="$simpin"; fi + if [ ! -z "$hostip" ]; then host="$hostip"; fi + if [ ! -z "$username" ]; then user="$username"; fi + if [ ! -z "$password" ]; then pw="$password"; fi + if [ ! -z "$simpin" ]; then pin="$simpin"; fi } if [ -z "$1" ]; then echo "none"; exit; fi opt="${1,,}" shift +hostip="192.168.8.1" while [ -n "$1" ]; do case "$1" in - -u|--user) username="$2"; shift ;; - -P|--password) password="$2"; shift ;; - -p|--pin) simpin="$2"; shift ;; - -h|--host) hostip="$2"; shift ;; + -u|--user) username="$2"; shift ;; + -P|--password) password="$2"; shift ;; + -p|--pin) simpin="$2"; shift ;; + -h|--host) hostip="$2"; shift ;; esac shift done status="no valid option given" result="none" +hostip="192.168.8.1" if [ "$opt" = "connected" ]; then source /usr/local/sbin/huawei_hilink_api.sh - if ! _initHilinkAPI; then echo "none"; exit; fi _setAPIParams + if ! _initHilinkAPI; then echo "none"; exit; fi result=$(_getMobileDataStatus) _closeHilinkAPI else - info_file="/tmp/huawei_infos_$host.dat" + info_file="/tmp/huawei_infos_${hostip}_${id -u}.dat" if [ -f "$info_file" ]; then age=$(( $(date +%s) - $(stat $info_file -c %Y) )) if [[ $age -gt 5 ]]; then rm -f $info_file; fi @@ -52,11 +54,11 @@ else infos=$(cat $info_file) else source /usr/local/sbin/huawei_hilink_api.sh + _setAPIParams if ! _initHilinkAPI; then echo "none"; exit; fi - _setAPIParams infos=$(_getAllInformations) _closeHilinkAPI - if [ ! -z "$infos" ]; then echo "$infos" > /tmp/huawei_infos_$host.dat; fi + if [ ! -z "$infos" ]; then echo "$infos" > $info_file; fi fi case "$opt" in diff --git a/includes/get_clients.php b/includes/get_clients.php index 11a74e943..279fbef80 100644 --- a/includes/get_clients.php +++ b/includes/get_clients.php @@ -284,7 +284,7 @@ function setClientState($state) $ipadd = $ipadd[0].'1'; // ip address of the Hilink api $mode = ($state == "up") ? 1 : 0; $pin=$user=$pw=""; - getMobileLogin($pin,$pw,$user) + getMobileLogin($pin,$pw,$user); exec('sudo '.RASPI_CLIENT_SCRIPT_PATH.'/onoff_huawei_hilink.sh -c '.$mode.' -h '.$ipadd.' '.$pin.' '.$user.' '.$pw); break; case "ppp": From 29547b52e1d4a7a2c7d708acb911bea525e912a9 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Sun, 23 May 2021 09:04:08 +0200 Subject: [PATCH 188/300] Use Hilink API --- config/client_config/info_huawei.sh | 2 +- config/client_config/info_huawei_hilink.sh | 10 +++++----- config/client_config/switchClientState.sh | 8 +++++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/config/client_config/info_huawei.sh b/config/client_config/info_huawei.sh index 540ede98c..fe6c27a13 100644 --- a/config/client_config/info_huawei.sh +++ b/config/client_config/info_huawei.sh @@ -47,7 +47,7 @@ case $opt in if [ ! "$res" = "none" ]; then if [ "$type" = "hilink" ]; then if [ "$res" = "LTE" ]; then res="4G" - elif [ "$res" = "WCDMA" ]]; then res="3G"; + elif [ "$res" = "WCDMA" ]; then res="3G"; else res="2G"; fi else if [ $res -eq 7 ]; then res="4G" diff --git a/config/client_config/info_huawei_hilink.sh b/config/client_config/info_huawei_hilink.sh index 24145bef8..32b279546 100644 --- a/config/client_config/info_huawei_hilink.sh +++ b/config/client_config/info_huawei_hilink.sh @@ -21,7 +21,7 @@ function _setAPIParams() { } if [ -z "$1" ]; then echo "none"; exit; fi -opt="${1,,}" +property="${1,,}" shift hostip="192.168.8.1" while [ -n "$1" ]; do @@ -47,7 +47,7 @@ else info_file="/tmp/huawei_infos_${hostip}_${id -u}.dat" if [ -f "$info_file" ]; then age=$(( $(date +%s) - $(stat $info_file -c %Y) )) - if [[ $age -gt 5 ]]; then rm -f $info_file; fi + if [[ $age -gt 10 ]]; then rm -f $info_file; fi fi if [ -f "$info_file" ]; then @@ -58,10 +58,10 @@ else if ! _initHilinkAPI; then echo "none"; exit; fi infos=$(_getAllInformations) _closeHilinkAPI - if [ ! -z "$infos" ]; then echo "$infos" > $info_file; fi + if [ ! -z "$infos" ]; then echo -n "$infos" > $info_file; fi fi - case "$opt" in + case "$property" in device|devicename) key="devicename" ;; @@ -84,7 +84,7 @@ else key="fullname" ;; *) - key="" + key="device" ;; esac if [ -z "$key" ]; then result="none"; fi diff --git a/config/client_config/switchClientState.sh b/config/client_config/switchClientState.sh index 4d8abbf20..acdd28928 100644 --- a/config/client_config/switchClientState.sh +++ b/config/client_config/switchClientState.sh @@ -7,8 +7,10 @@ # get webroot webroot=$(cat /etc/lighttpd/lighttpd.conf | sed -rn 's/server.document-root\s*=\s*\"(.*)\"\s*$/\1/p') -if [ -z "$webroot" ] || [ ! -d "$webroot" ]; then - exit +webuser=$(cat /etc/lighttpd/lighttpd.conf | sed -rn 's/server.username\s*=\s*\"(.*)\"\s*$/\1/p') +if [ -z "$webroot" ] || [ ! -d "$webroot" ] || [ -z "$webuser" ]; then + echo "$0 : Problem to obtain webroot directory and/or web user - exit" | systemd-cat + exit fi cd $webroot @@ -21,7 +23,7 @@ fi [ -z "$state" ] && exit -php << _EOF_ +sudo -u $webuser php << _EOF_ Date: Thu, 27 May 2021 00:53:00 +0000 Subject: [PATCH 189/300] Bump browserslist from 4.7.0 to 4.16.6 Bumps [browserslist](https://github.com/browserslist/browserslist) from 4.7.0 to 4.16.6. - [Release notes](https://github.com/browserslist/browserslist/releases) - [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md) - [Commits](https://github.com/browserslist/browserslist/compare/4.7.0...4.16.6) Signed-off-by: dependabot[bot] --- yarn.lock | 55 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4cad17aa3..ed917733e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -566,13 +566,15 @@ browser-sync@^2.26.7: yargs "6.4.0" browserslist@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.7.0.tgz#9ee89225ffc07db03409f2fee524dc8227458a17" - integrity sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA== + version "4.16.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" + integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== dependencies: - caniuse-lite "^1.0.30000989" - electron-to-chromium "^1.3.247" - node-releases "^1.1.29" + caniuse-lite "^1.0.30001219" + colorette "^1.2.2" + electron-to-chromium "^1.3.723" + escalade "^3.1.1" + node-releases "^1.1.71" bs-recipes@1.3.4: version "1.3.4" @@ -642,10 +644,10 @@ camelcase@^5.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30000998: - version "1.0.30000999" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000999.tgz#427253a69ad7bea4aa8d8345687b8eec51ca0e43" - integrity sha512-1CUyKyecPeksKwXZvYw0tEoaMCo/RwBlXmEtN5vVnabvO0KPd9RQLcaAuR9/1F+KDMv6esmOFWlsXuzDk+8rxg== +caniuse-lite@^1.0.30000998, caniuse-lite@^1.0.30001219: + version "1.0.30001230" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz#8135c57459854b2240b57a4a6786044bdc5a9f71" + integrity sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ== caseless@~0.12.0: version "0.12.0" @@ -837,6 +839,11 @@ color-support@^1.1.3: resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== +colorette@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" + integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== + combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -1171,10 +1178,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.3.247: - version "1.3.277" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.277.tgz#38b7b297f9b3f67ea900a965c1b11a555de526ec" - integrity sha512-Czmsrgng89DOgJlIknnw9bn5431QdtnUwGp5YYiPwU1DbZQUxCLF+rc1ZC09VNAdalOPcvH6AE8BaA0H5HjI/w== +electron-to-chromium@^1.3.723: + version "1.3.739" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.739.tgz#f07756aa92cabd5a6eec6f491525a64fe62f98b9" + integrity sha512-+LPJVRsN7hGZ9EIUUiWCpO7l4E3qBYHNadazlucBfsXBbccDFNKUBAgzE68FnkWGJPwD/AfKhSzL+G+Iqb8A4A== emoji-regex@^7.0.1: version "7.0.3" @@ -1309,6 +1316,11 @@ es6-weak-map@^2.0.1: es6-iterator "^2.0.3" es6-symbol "^3.1.1" +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -2915,12 +2927,10 @@ node-pre-gyp@^0.12.0: semver "^5.3.0" tar "^4" -node-releases@^1.1.29: - version "1.1.34" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.34.tgz#ced4655ee1ba9c3a2c5dcbac385e19434155fd40" - integrity sha512-fNn12JTEfniTuCqo0r9jXgl44+KxRH/huV7zM/KAGOKxDKrHr6EbT7SSs4B+DNxyBE2mks28AD+Jw6PkfY5uwA== - dependencies: - semver "^6.3.0" +node-releases@^1.1.71: + version "1.1.72" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe" + integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw== node-sass@^4.8.3: version "4.14.1" @@ -3786,11 +3796,6 @@ semver-greatest-satisfied-range@^1.1.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - semver@^7.3.2: version "7.3.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" From 083e19d631dbbc87fb662900622b51db709a091d Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 5 Jun 2021 10:16:35 +0100 Subject: [PATCH 190/300] Update About page insiders logo --- app/img/insiders.png | Bin 4122 -> 7949 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/app/img/insiders.png b/app/img/insiders.png index b29574f747ee8e91a9331bdf14cd7b36554bc086..6ad27f35e857da0d80e0ec2842480f21f15be33b 100644 GIT binary patch literal 7949 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 literal 4122 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 From eed50706d9148ed42bc15867be35db4ea626478c Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 5 Jun 2021 12:20:58 +0100 Subject: [PATCH 191/300] Fixup template merge from upstream master --- templates/openvpn.php | 59 ------------------------------------------- 1 file changed, 59 deletions(-) diff --git a/templates/openvpn.php b/templates/openvpn.php index 40e084e88..4e0adaba8 100755 --- a/templates/openvpn.php +++ b/templates/openvpn.php @@ -46,65 +46,6 @@ -
-

-
-
-
-
-
-
-
-
-
-
-
-
- - -
-
-
-
- - -
-
-
-
-
- - -
-
-
-
-
- -
-
-
-
-

-
-
- '; - ?> -
-
-
- - - ' , PHP_EOL; - } else { - echo '' , PHP_EOL; - } - ?> - - -
From 43e9a093c4385bfd67ac21daf9689b9622f24ded Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 6 Jun 2021 20:47:30 +0100 Subject: [PATCH 192/300] Add RASPI_OPENVPN_CLIENT_PATH --- config/config.php | 2 +- includes/defaults.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.php b/config/config.php index 8003a3206..44a46076a 100755 --- a/config/config.php +++ b/config/config.php @@ -18,9 +18,9 @@ define('RASPI_WPA_SUPPLICANT_CONFIG', '/etc/wpa_supplicant/wpa_supplicant.conf'); define('RASPI_HOSTAPD_CTRL_INTERFACE', '/var/run/hostapd'); define('RASPI_WPA_CTRL_INTERFACE', '/var/run/wpa_supplicant'); +define('RASPI_OPENVPN_CLIENT_PATH', '/etc/openvpn/client/'); 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'); diff --git a/includes/defaults.php b/includes/defaults.php index 1c87e4a50..cba04a1bc 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -23,9 +23,9 @@ 'RASPI_WPA_SUPPLICANT_CONFIG' => '/etc/wpa_supplicant/wpa_supplicant.conf', 'RASPI_HOSTAPD_CTRL_INTERFACE' => '/var/run/hostapd', 'RASPI_WPA_CTRL_INTERFACE' => '/var/run/wpa_supplicant', + 'RASPI_OPENVPN_CLIENT_PATH' => '/etc/openvpn/client/', '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', From 55995c797c7c3d81d9d9b9f58c1b06bbcdacce2b Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 6 Jun 2021 20:48:23 +0100 Subject: [PATCH 193/300] Update template, remove affilate link --- templates/openvpn/general.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/templates/openvpn/general.php b/templates/openvpn/general.php index 39573ce61..9398884ea 100644 --- a/templates/openvpn/general.php +++ b/templates/openvpn/general.php @@ -64,9 +64,7 @@ -
- -
+
From 234f22117f34c48a928fdf4354fe0bf5cb589418 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 6 Jun 2021 20:49:09 +0100 Subject: [PATCH 194/300] Remove file_move_config (deprecated) --- includes/functions.php | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/includes/functions.php b/includes/functions.php index 0b1a11c4e..93c609a35 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -270,28 +270,6 @@ function file_get_meta($filename, $pattern) } } -/** - * Renames an openvpn client config with the 'filename' header comment - * - * @param string file - * @return boolean - */ -function file_move_config($file) -{ - if(file_exists($file)) { - $file_data = file_get_contents($file); - preg_match('/^#\sfilename\s(.*)/i', $file_data, $matched); - $renamed = pathinfo($file, PATHINFO_DIRNAME).'/'. - $matched[1] .'_'.pathinfo($file, PATHINFO_FILENAME).'.'. - pathinfo($file, PATHINFO_EXTENSION); - if (!file_exists($renamed)) { - $return = system("sudo mv $file $renamed", $return); - } else { - return false; - } - } -} - /** * Callback function for array_filter * From 1647aa3c737f182e54d6c175b6502d46349525ce Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 6 Jun 2021 20:50:20 +0100 Subject: [PATCH 195/300] Refactor config handling w/ symbolic links --- includes/openvpn.php | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/includes/openvpn.php b/includes/openvpn.php index c5b00d1c4..711c1c450 100755 --- a/includes/openvpn.php +++ b/includes/openvpn.php @@ -53,7 +53,7 @@ function DisplayOpenVPNConfig() $authUser = current($auth); $authPassword = next($auth); } - $clients = preg_grep('/client.(conf)$/', scandir(pathinfo(RASPI_OPENVPN_CLIENT_CONFIG, PATHINFO_DIRNAME))); + $clients = preg_grep('/_client.(conf)$/', scandir(pathinfo(RASPI_OPENVPN_CLIENT_CONFIG, PATHINFO_DIRNAME))); $logEnable = 0; if (!empty($_POST) && !isset($_POST['log-openvpn'])) { @@ -158,36 +158,34 @@ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword) throw new RuntimeException('Unable to move uploaded file'); } - // Good file upload, update auth credentials if present - $prepend = '# filename '.pathinfo($file['name'], PATHINFO_FILENAME) .PHP_EOL; if (!empty($authUser) && !empty($authPassword)) { $auth_flag = 1; // Move tmp authdata to /etc/openvpn/login.conf $auth.= $authUser .PHP_EOL . $authPassword .PHP_EOL; file_put_contents($tmp_authdata, $auth); - file_prepend_data($tmp_authdata, $prepend); - file_move_config(RASPI_OPENVPN_CLIENT_LOGIN); chmod($tmp_authdata, 0644); - system("sudo cp $tmp_authdata " . RASPI_OPENVPN_CLIENT_LOGIN, $return); + $client_auth = RASPI_OPENVPN_CLIENT_PATH.pathinfo($file['name'], PATHINFO_FILENAME).'_login.conf'; + system("sudo cp $tmp_authdata $client_auth", $return); + system("sudo rm ".RASPI_OPENVPN_CLIENT_LOGIN, $return); + system("sudo ln -s $client_auth ".RASPI_OPENVPN_CLIENT_LOGIN, $return); if ($return !=0) { $status->addMessage('Unable to save client auth credentials', 'danger'); } } - // Prepend filname tag to .ovpn client config - file_prepend_data($tmp_ovpnclient, $prepend); - // Set iptables rules and, optionally, auth-user-pass exec("sudo /etc/raspap/openvpn/configauth.sh $tmp_ovpnclient $auth_flag " .$_SESSION['ap_interface'], $return); foreach ($return as $line) { $status->addMessage($line, 'info'); } - // Copy tmp client config to /etc/openvpn/client - file_move_config(RASPI_OPENVPN_CLIENT_CONFIG); + $client_ovpn = RASPI_OPENVPN_CLIENT_PATH.pathinfo($file['name'], PATHINFO_FILENAME).'_client.conf'; chmod($tmp_ovpnclient, 0644); - system("sudo cp $tmp_ovpnclient " . RASPI_OPENVPN_CLIENT_CONFIG, $return); + system("sudo cp $tmp_ovpnclient $client_ovpn", $return); + system("sudo rm ".RASPI_OPENVPN_CLIENT_CONFIG, $return); + system("sudo ln -s $client_ovpn ".RASPI_OPENVPN_CLIENT_CONFIG, $return); + if ($return ==0) { $status->addMessage('OpenVPN client.conf uploaded successfully', 'info'); } else { From 1b7074f693f4e2603c8a1f214241a7cc34ad6375 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 6 Jun 2021 20:51:13 +0100 Subject: [PATCH 196/300] Revise openvpn log output method --- installers/openvpnlog.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installers/openvpnlog.sh b/installers/openvpnlog.sh index 96e79e2dc..d933d6695 100755 --- a/installers/openvpnlog.sh +++ b/installers/openvpnlog.sh @@ -1,3 +1,3 @@ #!/bin/bash touch /tmp/openvpn.log -grep -m 100 openvpn /var/log/syslog | sudo tee /tmp/openvpn.log +journalctl |grep -m 200 openvpn | sudo tee /tmp/openvpn.log From cca50c35920c8669a624c742667311fc21faeaf1 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 6 Jun 2021 20:53:21 +0100 Subject: [PATCH 197/300] Update raspap.sudoers w/ /usr/bin/ln -s --- installers/raspap.sudoers | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index 0886a2ac2..260a45fc3 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -20,9 +20,9 @@ www-data ALL=(ALL) NOPASSWD:/bin/systemctl start openvpn-client@client www-data ALL=(ALL) NOPASSWD:/bin/systemctl enable openvpn-client@client www-data ALL=(ALL) NOPASSWD:/bin/systemctl stop openvpn-client@client www-data ALL=(ALL) NOPASSWD:/bin/systemctl disable openvpn-client@client -www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/ovpnclient.ovpn /etc/openvpn/client/client.conf -www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/authdata /etc/openvpn/client/login.conf -www-data ALL=(ALL) NOPASSWD:/bin/mv /etc/openvpn/client/*.conf /etc/openvpn/client/*.conf +www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/ovpnclient.ovpn /etc/openvpn/client/*.conf +www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/authdata /etc/openvpn/client/*.conf +www-data ALL=(ALL) NOPASSWD:/usr/bin/ln -s /etc/openvpn/client/*.conf /etc/openvpn/client/*.conf www-data ALL=(ALL) NOPASSWD:/bin/rm /etc/openvpn/client/*.conf www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/dnsmasqdata /etc/dnsmasq.d/090_*.conf www-data ALL=(ALL) NOPASSWD:/bin/rm /etc/dnsmasq.d/090_*.conf From 83c6e17970039898d7fc14ecb540341c164b6ab8 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 6 Jun 2021 20:55:43 +0100 Subject: [PATCH 198/300] Refactor openvpn activate cfg ajax --- ajax/openvpn/activate_ovpncfg.php | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/ajax/openvpn/activate_ovpncfg.php b/ajax/openvpn/activate_ovpncfg.php index 2a8542a94..235f01eb5 100644 --- a/ajax/openvpn/activate_ovpncfg.php +++ b/ajax/openvpn/activate_ovpncfg.php @@ -5,21 +5,14 @@ if (isset($_POST['cfg_id'])) { $ovpncfg_id = $_POST['cfg_id']; - $ovpncfg_path = pathinfo(RASPI_OPENVPN_CLIENT_CONFIG, PATHINFO_DIRNAME).'/'; - $ovpncfg_files = $ovpncfg_path .$ovpncfg_id.'_*.conf'; + $ovpncfg_client = RASPI_OPENVPN_CLIENT_PATH.$ovpncfg_id.'_client.conf'; + $ovpncfg_login = RASPI_OPENVPN_CLIENT_PATH.$ovpncfg_id.'_login.conf'; - // move currently active profile - $meta = file_get_meta(RASPI_OPENVPN_CLIENT_CONFIG,'#\sfilename\s(.*)'); - $ovpncfg_client = $ovpncfg_path .$meta.'_client.conf'; - $ovpncfg_login = $ovpncfg_path .$meta.'_login.conf'; - exec("sudo mv ".RASPI_OPENVPN_CLIENT_CONFIG." $ovpncfg_client", $return); - exec("sudo mv ".RASPI_OPENVPN_CLIENT_LOGIN." $ovpncfg_login", $return); - - // replace with selected profile - $ovpncfg_client = $ovpncfg_path .$ovpncfg_id.'_client.conf'; - $ovpncfg_login = $ovpncfg_path .$ovpncfg_id.'_login.conf'; - exec("sudo mv $ovpncfg_client ".RASPI_OPENVPN_CLIENT_CONFIG, $return); - exec("sudo mv $ovpncfg_login ".RASPI_OPENVPN_CLIENT_LOGIN, $return); + // remove existing client config +login and symbolically link the selected one + system("sudo rm ".RASPI_OPENVPN_CLIENT_CONFIG, $return); + system("sudo ln -s $ovpncfg_client ".RASPI_OPENVPN_CLIENT_CONFIG, $return); + system("sudo rm ".RASPI_OPENVPN_CLIENT_LOGIN, $return); + system("sudo ln -s $ovpncfg_login ".RASPI_OPENVPN_CLIENT_LOGIN, $return); // restart service exec("sudo /bin/systemctl stop openvpn-client@client", $return); From 5f7df3accb9d211cf69a01d214f1ff0c0fb63692 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 8 Jun 2021 20:16:34 +0100 Subject: [PATCH 199/300] Update release version --- README.md | 2 +- includes/defaults.php | 2 +- index.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8d2c15f45..29bc59258 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![](https://i.imgur.com/FRU1tXF.png) -[![Release 2.7.3](https://img.shields.io/badge/release-v2.7.3-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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/) +[![Release 2.7.4-beta](https://img.shields.io/badge/release-2.7.4beta-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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. diff --git a/includes/defaults.php b/includes/defaults.php index cba04a1bc..6db85dd58 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -6,7 +6,7 @@ $defaults = [ 'RASPI_BRAND_TEXT' => 'RaspAP', - 'RASPI_VERSION' => '2.7.3', + 'RASPI_VERSION' => '2.7.4-beta', 'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json', 'RASPI_ADMIN_DETAILS' => RASPI_CONFIG.'/raspap.auth', 'RASPI_WIFI_AP_INTERFACE' => 'wlan0', diff --git a/index.php b/index.php index 4f79b0627..71f914301 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.3 + * @version 2.7.4-beta * @link https://github.com/raspap/raspap-insiders/ * @link https://raspap.com/ * @see http://sirlagz.net/2013/02/08/raspap-webgui/ From e62bb9c04e70d175e0e0301f941346ff1f8f7cf0 Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Sat, 12 Jun 2021 14:28:09 +0200 Subject: [PATCH 200/300] Fix display of active openvpn config --- templates/openvpn/configs.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/templates/openvpn/configs.php b/templates/openvpn/configs.php index dc624c63e..a79557391 100644 --- a/templates/openvpn/configs.php +++ b/templates/openvpn/configs.php @@ -7,6 +7,10 @@
openvpn-client
service.") ?>

+ Date: Sat, 12 Jun 2021 14:29:37 +0200 Subject: [PATCH 201/300] Fix openvpn activation of configuration --- app/js/custom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/js/custom.js b/app/js/custom.js index b834aa308..4ba0436b0 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -291,7 +291,7 @@ $('#ovpn-confirm-delete').on('show.bs.modal', function (e) { }); $('#ovpn-confirm-activate').on('click', '.btn-activate', function (e) { - var cfg_id = $(this).data('recordId'); + var cfg_id = $(this).data('record-id'); $.post('ajax/openvpn/activate_ovpncfg.php',{'cfg_id':cfg_id},function(data){ jsonData = JSON.parse(data); $("#ovpn-confirm-activate").modal('hide'); From a963c0564f2a407ae70821b748ca9a556bb11139 Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Sat, 12 Jun 2021 14:36:03 +0200 Subject: [PATCH 202/300] Correct displayed configuration name --- templates/openvpn/configs.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/openvpn/configs.php b/templates/openvpn/configs.php index a79557391..dad4a7952 100644 --- a/templates/openvpn/configs.php +++ b/templates/openvpn/configs.php @@ -12,14 +12,14 @@ $conf_default = empty($ret) ? "none" : $ret[0]; ?> + } + $label = preg_replace('/_client$/','',pathinfo($client, PATHINFO_FILENAME)); + $client = $label; + ?>
From 4bfbfc40f1130bf787f91d5938bbde6a43223b6d Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Sat, 12 Jun 2021 16:19:31 +0200 Subject: [PATCH 203/300] Remove determination of the default config --- templates/openvpn/configs.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/templates/openvpn/configs.php b/templates/openvpn/configs.php index dad4a7952..c3e578eec 100644 --- a/templates/openvpn/configs.php +++ b/templates/openvpn/configs.php @@ -7,10 +7,6 @@
openvpn-client
service.") ?>

- Date: Sat, 12 Jun 2021 16:21:20 +0200 Subject: [PATCH 204/300] Insert determination of the default config --- includes/openvpn.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/includes/openvpn.php b/includes/openvpn.php index 711c1c450..67084df4f 100755 --- a/includes/openvpn.php +++ b/includes/openvpn.php @@ -54,6 +54,8 @@ function DisplayOpenVPNConfig() $authPassword = next($auth); } $clients = preg_grep('/_client.(conf)$/', scandir(pathinfo(RASPI_OPENVPN_CLIENT_CONFIG, PATHINFO_DIRNAME))); + exec("readlink ".RASPI_OPENVPN_CLIENT_CONFIG." | xargs basename", $ret); + $conf_default = empty($ret) ? "none" : $ret[0]; $logEnable = 0; if (!empty($_POST) && !isset($_POST['log-openvpn'])) { @@ -79,7 +81,8 @@ function DisplayOpenVPNConfig() "public_ip", "authUser", "authPassword", - "clients" + "clients", + "conf_default" ) ); } From e07774c09b3c28a95f91162b71b61dea4978fd67 Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 12 Jun 2021 20:08:12 +0100 Subject: [PATCH 205/300] Update fr_FR locale translations, thx @yolateng0 --- locale/fr_FR/LC_MESSAGES/messages.mo | Bin 19293 -> 25707 bytes locale/fr_FR/LC_MESSAGES/messages.po | 173 ++++++++++++++++++++++++++- 2 files changed, 169 insertions(+), 4 deletions(-) diff --git a/locale/fr_FR/LC_MESSAGES/messages.mo b/locale/fr_FR/LC_MESSAGES/messages.mo index 2c4e8bfef1cc938d6d8964c7cd7776953dfda039..c86cdd43b8a39d2e4fac7592ed57c8b7366ab716 100644 GIT binary patch literal 25707 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\n" "POT-Creation-Date: 2017-10-19 08:56+0000\n" -"PO-Revision-Date: 2021-02-12 14:34\n" -"Last-Translator: yolateng0 https://github.com/yolateng0\n" +"PO-Revision-Date: 2021-06-12 19:03\n" +"Last-Translator: Bill Zimmerman \n" "Language-Team: French\n" "Language: fr_FR\n" "MIME-Version: 1.0\n" @@ -19,13 +19,13 @@ msgstr "" #: index.php msgid "RaspAP Wifi Configuration Portal" -msgstr "RaspAP Wifi Portail de Configuration" +msgstr "RaspAP Configuration de Portail Wifi" msgid "Toggle navigation" msgstr "Basculer la navigation" msgid "RaspAP Wifi Portal" -msgstr "RaspAP Wifi Portail" +msgstr "RaspAP Portail Wifi" msgid "Dashboard" msgstr "Tableau de bord" @@ -361,6 +361,27 @@ msgstr "Journaliser les requêtes DHCP" msgid "Log DNS queries" msgstr "Journaliser les requêtes DNS" +msgid "Restrict access" +msgstr "Accès restreint" + +msgid "Limit network access to static clients" +msgstr "Limiter l'accès au réseau aux clients baux statiques" + +msgid "Enable this option if you want RaspAP to ignore any clients which are not specified in the static leases list." +msgstr "Activer cette option si tu veux que RaspAP ignore les clients qui ne sont pas spécifiés dans la liste des baux statiques." + +msgid "This option adds dhcp-ignore to the dnsmasq configuration." +msgstr "Cette option ajoute no-resolv à la configuration dnsmasq." + +msgid "Clients with a particular hardware MAC address can always be allocated the same IP address." +msgstr "Les clients avec une adresse MAC identifiée peuvent toujours être alloués à la même adresse IP." + +msgid "This option adds dhcp-host entries to the dnsmasq configuration." +msgstr "Cette option ajoute no-resolv à la configuration Dnsmasq." + +msgid "This toggles the gateway/nogateway option for this interface in the DHCPCD configuration." +msgstr "Cela bascule l'option gateway/nogateway pour cette interface dans la configuration de DHCPCD." + #: includes/hostapd.php msgid "Basic" msgstr "De base" @@ -676,6 +697,66 @@ msgstr "Essai de démarrage d'openvpn" msgid "Attempting to stop openvpn" msgstr "Essai d'arrêt d'openvpn" +msgid "Configurations" +msgstr "Configuration" + +msgid "Currently available OpenVPN client configurations are displayed below." +msgstr "Les configurations des fichiers OpenVPN actuellement disponibles sont affichées ci-dessous." + +msgid "Activating a configuraton will restart the openvpn-client service." +msgstr "L'activation d'une configuration redémarrera le service openvpn-client." + +msgid "Delete OpenVPN client" +msgstr "Supprimer le fichier client OpenVPN" + +msgid "Delete client configuration? This cannot be undone." +msgstr "Supprimer ce fichier ? Opération irréversible." + +msgid "Activate OpenVPN client" +msgstr "Activer le fichier OpenVPN" + +msgid "Activate client configuration? This will restart the openvpn-client service." +msgstr "L'activation d'une configuration redémarrera le service openvpn-client." + +msgid "Activate" +msgstr "Activer" + +msgid "Cancel" +msgstr "Annuler" + +msgid "Enable this option to log openvpn activity." +msgstr "Activer cette option pour enregistrer l'activité openvpn." + +msgid "Authentification Method" +msgstr "Méthode d'identification" + +msgid "Username and password" +msgstr "Nom d'utilisateur et mot de passe" + +msgid "Certificates" +msgstr "Certificats" + +msgid "Enter username and password" +msgstr "Entrez votre nom d'utilisateur et votre mot de passe" + +msgid "Certificates in the configuration file" +msgstr "Certificats dans le fichier de configuration" + +msgid "RaspAP supports certificates by including them in the configuration file." +msgstr "RaspAP prend en charge les certificats en les incluant dans le fichier de configuration." + +msgid "Signing certification authority (CA) certificate (e.g. ca.crt): enclosed in <ca> ... </ca> tags." +msgstr "Certificat d'autorité de certification (CA) de signature (par exemple ca.crt) : joint dans <ca> ... </ca> balises." + +msgid "Client certificate (public key) (e.g. client.crt): enclosed in <cert> ... </cert> tags." +msgstr "Certificat client (clé publique) (par exemple client.crt) : inclus dans <cert> ... </cert> balises." + +msgid "Private key of the client certificate (e.g. client.key): enclosed in <key> ... </key> tags." +msgstr "Clé privée du certificat client (par exemple client.key) : entourée de balises <clé> ... </key>." + +msgid "Configuration File" +msgstr "Fichier de configuration" + #: includes/torproxy.php msgid "TOR is not running" msgstr "TOR n'est pas en cours d'exécution" @@ -799,3 +880,87 @@ msgstr "Une adresse IP personnalisée non valide a été trouvée sur la ligne " msgid "Invalid custom host found on line " msgstr "Un Host personnalisée non valide a été trouvée sur la ligne " +msgid "Tunnel settings" +msgstr "Paramètres du tunnel" + +msgid "Enable server" +msgstr "Activer le serveur" + +msgid "Enable this option to encrypt traffic by creating a tunnel between RaspAP and configured peers." +msgstr "Active cette option pour chiffrer le trafic en créant un tunnel entre RaspAP et les pairs configurés." + +msgid "This option adds wg0.conf to the WireGuard configuration." +msgstr "Cette option ajoute wg0.conf à la configuration de WireGuard." + +msgid "Local public key" +msgstr "Clé publique locale" + +msgid "Local Port" +msgstr "Port local" + +msgid "IP Address" +msgstr "Adresse IP" + +msgid "DNS" +msgstr "DNS" + +msgid "Peer" +msgstr "Pair" + +msgid "Enable peer" +msgstr "Activer le pair" + +msgid "Enable this option to encrypt traffic by creating a tunnel between RaspAP and this peer." +msgstr "Active cette option pour chiffrer le trafic en créant un tunnel entre RaspAP et ce pair." + +msgid "This option adds client.conf to the WireGuard configuration." +msgstr "Cette option ajoute client.conf à la configuration de WireGuard." + +msgid "Peer public key" +msgstr "Clé publique du pair" + +msgid "Endpoint address" +msgstr "Adresse de terminaison" + +msgid "Allowed IPs" +msgstr "Adresses IP autorisées" + +msgid "Persistent keepalive" +msgstr "Keepalive persistant" + +msgid "Display WireGuard status" +msgstr "Afficher le statut WireGuard" + +msgid "Enable this option to display an updated WireGuard status." +msgstr "Activer cette option pour afficher un statut WireGuard mis à jour." + +msgid "Scan this QR code with your client to connect to this tunnel" +msgstr "Scannez ce code QR avec votre client pour vous connecter à ce tunnel" + +msgid "or download the client.conf file to your device." +msgstr "ou téléchargez le fichier client.conf sur votre appareil." + +msgid "Download" +msgstr "Télécharger" + +msgid "Start WireGuard" +msgstr "Démarrer WireGuard" + +msgid "Stop WireGuard" +msgstr "Arrêter WireGuard" + +msgid "Information provided by wireguard" +msgstr "Informations fournies par WireGuard" + +msgid "Attempting to start WireGuard" +msgstr "Tentative de démarrage de WireGuard" + +msgid "Attempting to stop WireGuard" +msgstr "Tentative d’arrêt de WireGuard" + +msgid "WireGuard configuration updated successfully" +msgstr "Configuration de WireGuard mise à jour avec succès" + +msgid "WireGuard configuration failed to be updated" +msgstr "La configuration WireGuard n'a pas pu être mise à jour" + From 815e4639a1bcd20e8c8c12a268490fe6e9db1db7 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 15 Jun 2021 16:03:45 +0100 Subject: [PATCH 206/300] Update release version --- README.md | 2 +- includes/defaults.php | 2 +- index.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 29bc59258..cf2d990e6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![](https://i.imgur.com/FRU1tXF.png) -[![Release 2.7.4-beta](https://img.shields.io/badge/release-2.7.4beta-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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/) +[![Release 2.7.4](https://img.shields.io/badge/release-2.7.4-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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. diff --git a/includes/defaults.php b/includes/defaults.php index 6db85dd58..280d1c3ef 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -6,7 +6,7 @@ $defaults = [ 'RASPI_BRAND_TEXT' => 'RaspAP', - 'RASPI_VERSION' => '2.7.4-beta', + 'RASPI_VERSION' => '2.7.4', 'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json', 'RASPI_ADMIN_DETAILS' => RASPI_CONFIG.'/raspap.auth', 'RASPI_WIFI_AP_INTERFACE' => 'wlan0', diff --git a/index.php b/index.php index 71f914301..0f9bb7a66 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.4-beta + * @version 2.7.4 * @link https://github.com/raspap/raspap-insiders/ * @link https://raspap.com/ * @see http://sirlagz.net/2013/02/08/raspap-webgui/ From bcff66d14fd97d6131cc291205aae0089666f9a5 Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Tue, 15 Jun 2021 18:05:36 +0200 Subject: [PATCH 207/300] Update issue templates --- .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..bbcbbe7d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From 6f353b331ff493c34789dbee8937bec085836316 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Jun 2021 14:55:24 +0000 Subject: [PATCH 208/300] Bump postcss from 7.0.18 to 7.0.36 Bumps [postcss](https://github.com/postcss/postcss) from 7.0.18 to 7.0.36. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/7.0.18...7.0.36) --- updated-dependencies: - dependency-name: postcss dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index ed917733e..c30bcf3ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3383,9 +3383,9 @@ postcss-value-parser@^4.0.2: integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ== postcss@^7.0.17, postcss@^7.0.18: - version "7.0.18" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.18.tgz#4b9cda95ae6c069c67a4d933029eddd4838ac233" - integrity sha512-/7g1QXXgegpF+9GJj4iN7ChGF40sYuGYJ8WZu8DZWnmhQ/G36hfdk3q9LBJmoK+lZ+yzZ5KYpOoxq7LF1BxE8g== + version "7.0.36" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.36.tgz#056f8cffa939662a8f5905950c07d5285644dfcb" + integrity sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw== dependencies: chalk "^2.4.2" source-map "^0.6.1" From 0625fcc5ef4ac2e6e085628df059b3028eda5034 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Fri, 18 Jun 2021 14:04:56 +0200 Subject: [PATCH 209/300] Add raspap_helpers.sh --- config/client_config/huawei_hilink_api.sh | 281 ++++++++++-------- config/client_config/info_huawei_hilink.sh | 12 +- config/client_config/onoff_huawei_hilink.sh | 28 +- config/client_config/raspap_helpers.sh | 51 ++++ ...k.service => start_huawei_hilink@.service} | 2 +- config/client_config/switchClientState.sh | 36 --- config/client_udev_prototypes.json | 4 +- installers/raspap.sudoers | 1 - 8 files changed, 233 insertions(+), 182 deletions(-) create mode 100644 config/client_config/raspap_helpers.sh rename config/client_config/{start_huawei_hilink.service => start_huawei_hilink@.service} (76%) delete mode 100644 config/client_config/switchClientState.sh diff --git a/config/client_config/huawei_hilink_api.sh b/config/client_config/huawei_hilink_api.sh index 5c9fd809b..6dc439f47 100644 --- a/config/client_config/huawei_hilink_api.sh +++ b/config/client_config/huawei_hilink_api.sh @@ -6,15 +6,16 @@ # - send a standard http request with a xml formatted string to the device (default IP 192.169.8.1) # - Howto: # o "source" this script in your own script from the command line -# o if host ip/name differs, set "host=192.168.178.1" before calling any function -# o if the device is locked by a password, set user="admin"; pw="1234secret" -# _login is called automaticallcall -# Password types 3 and 4 are supported -# o if the SIM is requiring a PIN, set "pin=1234" +# o if hilink_host ip/name differs, set "hilink_host=192.168.178.1" before calling any function +# o if the device is locked by a password, set hilink_user="admin"; hilink_password"1234secret" +# _login is called automatically +# only password type 4 is supported +# o if the SIM is requiring a PIN, set "hilink_pin=1234" # o connect device to network: _switchMobileData ON ( or 1 ) # o disconnect device: _switchMobileData OFF ( or 0 ) # o get informations about the device: _getDeviceInformation and _getStatus and _getNetProvider # all functions return XML formatted data in $response. +# o _getAllInformations: returns all available informations as key/value pairs (outputs text) # o Check if device is connected: "if _isConnected; then .... fi" # o $response can be parsed by calling _valueFromResponse # e.g "_valueFromResponse msisdn" to get the phone number after a call to _getDeviceInformation @@ -29,6 +30,7 @@ # # required software: curl, base64, sha256sum, sed # +# ToDo: improve error handling # # zbchristian 2021 # @@ -36,65 +38,73 @@ # Initialization procedure # ======================== # -# host=$host_default # ip address of device -# user="admin" # user name if locked (default admin) -# pw="1234Secret" # password if locked -# pin="1234" # PIN of SIM -# _initHilinkAPI # initialize the API +# hilink_host=192.168.8.1 # ip address of device +# hilink_user="admin" # user name if locked (default admin) +# hilink_password="1234Secret" # password if locked +# hilink_pin="1234" # PIN of SIM +# _initHilinkAPI # initialize the API # # Termination # =========== # cleanup the API before quitting the shell # _closeHilinkAPI (optional: add parameter "save" to save the session/token data for subsequent calls. Valid for a few minutes.) +# +# BE AWARE, THAT THE API USES SOME GLOBAL VARIABLES : hilink_host, user, password, pin, response, status +# USE THESE ONLY TO COMMUNICATE WITH THE API. +# DO NOT USE THE VARIABLE PRE_FIX "hilink_" FOR YOUR OWN VARIABLES +# -host_default="192.168.8.1" -save_file="/tmp/hilink_api_saved.dat" -save_age=60 -header_file="/tmp/hilink_login_hdr.txt" +hilink_host_default="192.168.8.1" +hilink_save_file="/tmp/hilink_api_saved.dat" +hilink_save_age=60 +hilink_header_file="/tmp/hilink_login_hdr.txt" # initialize function _initHilinkAPI() { - if [ -z "$host" ]; then host=$host_default; fi + local age + if [ -z "$hilink_host" ]; then hilink_host=$hilink_host_default; fi if ! _hostReachable; then return 1; fi - if [ -f $save_file ]; then # found file with saved data + if [ -f $hilink_save_file ]; then # found file with saved data _getSavedData - age=$(( $(date +%s) - $(stat $save_file -c %Y) )) - if [[ $age -gt $save_age ]]; then - rm -f $save_file + age=$(( $(date +%s) - $(stat $hilink_save_file -c %Y) )) + if [[ $age -gt $hilink_save_age ]]; then + rm -f $hilink_save_file _logout _sessToken fi fi - if [ -z "$sessID" ] || [ -z "$token" ]; then _sessToken; fi + if [ -z "$hilink_sessID" ] || [ -z "$hilink_token" ]; then _sessToken; fi _login return $? } function _getSavedData() { - if [ -f $save_file ]; then # restore saved session data - dat=$(cat $save_file) - sessID=$(echo "$dat" | sed -nr 's/sessionid: ([a-z0-9]*)/\1/ip') - token=$(echo "$dat" | sed -nr 's/token: ([a-z0-9]*)/\1/ip') - tokenlist=( $(echo "$dat" | sed -nr 's/tokenlist: ([a-z0-9 ]*)/\1/ip') ) + local dat + if [ -f $hilink_save_file ]; then # restore saved session data + dat=$(cat $hilink_save_file) + hilink_sessID=$(echo "$dat" | sed -nr 's/sessionid: ([a-z0-9]*)/\1/ip') + hilink_token=$(echo "$dat" | sed -nr 's/token: ([a-z0-9]*)/\1/ip') + hilink_tokenlist=( $(echo "$dat" | sed -nr 's/tokenlist: ([a-z0-9 ]*)/\1/ip') ) fi } # Cleanup # parameter: "save" - will store sessionid and tokens in file function _closeHilinkAPI() { - if [ -z "$host" ]; then host=$host_default; fi + local opt + if [ -z "$hilink_host" ]; then hilink_host=$hilink_host_default; fi if ! _hostReachable; then return 1; fi - rm -f $save_file + rm -f $hilink_save_file [ ! -z "$1" ] && opt="${1,,}" if [ ! -z "$opt" ] && [ "$opt" = "save" ]; then - echo "sessionid: $sessID" > $save_file - echo "token: $token" >> $save_file - echo "tokenlist: ${tokenlist[@]}" >> $save_file + echo "sessionid: $hilink_sessID" > $hilink_save_file + echo "token: $hilink_token" >> $hilink_save_file + echo "tokenlist: ${hilink_tokenlist[@]}" >> $hilink_save_file fi _logout - tokenlist="" - sessID="" - token="" + hilink_tokenlist="" + hilink_sessID="" + hilink_token="" return 0 } @@ -111,6 +121,7 @@ function _getStatus() { } function _isConnected() { + local conn conn=$(_getStatus "connectionstatus") status="NO" if [ ! -z "$conn" ] && [ $conn -eq 901 ]; then @@ -176,7 +187,7 @@ function _getMobileDataStatus() { } -# PIN of SIM can be passed either as $pin, or as parameter +# PIN of SIM can be passed either as $hilink_pin, or as parameter # parameter: PIN number of SIM card function _enableSIM() { #SimState: @@ -187,14 +198,15 @@ function _enableSIM() { #259 - check PIN, #260 - PIN required, #261 - PUK required - if [ ! -z "$1" ]; then pin="$1"; fi + local simstate + if [ ! -z "$1" ]; then hilink_pin="$1"; fi if ! _login; then return 1; fi if _sendRequest "api/pin/status"; then - simstate=`echo $response | sed -rn 's/.*([0-9]*)<\/simstate>.*/\1/pi'` + simstate=$(echo $response | sed -rn 's/.*([0-9]*)<\/simstate>.*/\1/pi') if [[ $simstate -eq 257 ]]; then status="SIM ready"; return 0; fi if [[ $simstate -eq 260 ]]; then - status="PIN required" - if [ ! -z "$pin" ]; then _setPIN "$pin"; fi + status="PIN required" + if [ ! -z "$hilink_pin" ]; then _setPIN "$hilink_pin"; fi return $? fi if [[ $simstate -eq 255 ]]; then status="NO SIM"; return 1; fi @@ -202,20 +214,20 @@ function _enableSIM() { return 1 } -# obtain session and verification token - stored in vars $sessID and $token +# obtain session and verification token - stored in vars $hilink_sessID and $token # parameter: none function _sessToken() { - tokenlist="" - token="" - sessID="" - response=$(curl -s http://$host/api/webserver/SesTokInfo -m 5 2> /dev/null) - if [ -z "$response" ]; then echo "No access to device at $host"; return 1; fi + hilink_tokenlist="" + hilink_token="" + hilink_sessID="" + response=$(curl -s http://$hilink_host/api/webserver/SesTokInfo -m 5 2> /dev/null) + if [ -z "$response" ]; then echo "No access to device at $hilink_host"; return 1; fi status=$(echo "$response" | sed -nr 's/.*([0-9]*)<\/code>.*/\1/ip') if [ -z "$status" ]; then - token=`echo $response | sed -r 's/.*(.*)<\/TokInfo>.*/\1/'` - sessID=`echo $response | sed -r 's/.*(.*)<\/SesInfo>.*/\1/'` - if [ ! -z "$sessID" ] && [ ! -z "$token" ]; then - sessID="SessionID=$sessID" + hilink_token=$(echo $response | sed -r 's/.*(.*)<\/TokInfo>.*/\1/') + hilink_sessID=$(echo $response | sed -r 's/.*(.*)<\/SesInfo>.*/\1/') + if [ ! -z "$hilink_sessID" ] && [ ! -z "$hilink_token" ]; then + hilink_sessID="SessionID=$hilink_sessID" return 0 fi fi @@ -223,38 +235,39 @@ function _sessToken() { } # unlock device (if locked) with user name and password -# requires stored user="admin"; pw="1234secret";host=$host_default +# requires stored hilink_user="admin"; hilink_password"1234secret";hilink_host=$hilink_host_default # parameter: none function _login() { + local ret encpw pwtype pwtype3 hashedpw pwtype4 if _loginState; then return 0; fi # login not required, or already done _sessToken # get password type if ! _sendRequest "api/user/state-login"; then return 1; fi pwtype=$(echo "$response" | sed -rn 's/.*([0-9])<\/password_type>.*/\1/pi') if [ -z "$pwtype" ];then pwtype=4; fi # fallback is type 4 - ret=1 - if [[ ! -z "$user" ]] && [[ ! -z "$pw" ]]; then + ret=1 + if [[ ! -z "$hilink_user" ]] && [[ ! -z "$hilink_password" ]]; then # password encoding # type 3 : base64(pw) encoded # type 4 : base64(sha256sum(user + base64(sha256sum(pw)) + token)) - pwtype3=$(echo -n "$pw" | base64 --wrap=0) - hashedpw=$(echo -n "$pw" | sha256sum -b | sed -nr 's/^([0-9a-z]*).*$/\1/ip' ) + pwtype3=$(echo -n "$hilink_password" | base64 --wrap=0) + hashedpw=$(echo -n "$hilink_password" | sha256sum -b | sed -nr 's/^([0-9a-z]*).*$/\1/ip' ) hashedpw=$(echo -n "$hashedpw" | base64 --wrap=0) - pwtype4=$(echo -n "$user$hashedpw$token" | sha256sum -b | sed -nr 's/^([0-9a-z]*).*$/\1/ip' ) + pwtype4=$(echo -n "$hilink_user$hashedpw$hilink_token" | sha256sum -b | sed -nr 's/^([0-9a-z]*).*$/\1/ip' ) encpw=$(echo -n "$pwtype4" | base64 --wrap=0) if [ $pwtype -ne 4 ]; then encpw=$pwtype3; fi - xmldata="$user$encpw$pwtype" - xtraopts="--dump-header $header_file" - rm -f $header_file + hilink_xmldata="$hilink_user$encpw$pwtype" + hilink_xtraopts="--dump-header $hilink_header_file" + rm -f $hilink_header_file _sendRequest "api/user/login" if [ ! -z "$status" ] && [ "$status" = "OK" ]; then # store the list of 30 tokens. Each token is valid for a single request - tokenlist=( $(cat $header_file | sed -rn 's/^__RequestVerificationToken:\s*([0-9a-z#]*).*$/\1/pi' | sed 's/#/ /g') ) + hilink_tokenlist=( $(cat $hilink_header_file | sed -rn 's/^__RequestVerificationToken:\s*([0-9a-z#]*).*$/\1/pi' | sed 's/#/ /g') ) _getToken - sessID=$(cat $header_file | grep -ioP 'SessionID=([a-z0-9]*)') - if [ ! -z "$sessID" ] && [ ! -z "$token" ]; then ret=0; fi + hilink_sessID=$(cat $hilink_header_file | grep -ioP 'SessionID=([a-z0-9]*)') + if [ ! -z "$hilink_sessID" ] && [ ! -z "$hilink_token" ]; then ret=0; fi fi - rm -f $header_file + rm -f $hilink_header_file fi return $ret } @@ -263,12 +276,12 @@ function _login() { # parameter: none function _logout() { if _loginState; then - xmldata="1" + hilink_xmldata="1" if _sendRequest "api/user/logout"; then - tokenlist="" - sessID="" - token="" - login_enabled="" + hilink_tokenlist="" + hilink_sessID="" + hilink_token="" + hilink_login_enabled="" fi return $? fi @@ -277,41 +290,44 @@ function _logout() { # parameter: none function _loginState() { - status="OK" - if [ -z "$login_enabled" ]; then _checkLoginEnabled; fi - if [ $login_enabled -eq 1 ]; then return 0; fi # login is disabled - _sendRequest "api/user/state-login" - state=`echo "$response" | sed -rn 's/.*(.*)<\/state>.*/\1/pi'` - if [ ! -z "$state" ] && [ $state -eq 0 ]; then # already logged in - return 0 - fi - return 1 + local state + status="OK" + if [ -z "$hilink_login_enabled" ]; then _checkLoginEnabled; fi + if [ $hilink_login_enabled -eq 1 ]; then return 0; fi # login is disabled + _sendRequest "api/user/state-login" + state=`echo "$response" | sed -rn 's/.*(.*)<\/state>.*/\1/pi'` + if [ ! -z "$state" ] && [ $state -eq 0 ]; then # already logged in + return 0 + fi + return 1 } function _checkLoginEnabled() { + local state if _sendRequest "api/user/hilink_login"; then - login_enabled=0 - state=$(echo $response | sed -rn 's/.*(.*)<\/hilink_login>.*/\1/pi') - if [ ! -z "$state" ] && [ $state -eq 0 ]; then # no login enabled - login_enabled=1 - fi - else - login_enabled="" - fi + hilink_login_enabled=0 + state=$(echo $response | sed -rn 's/.*(.*)<\/hilink_login>.*/\1/pi') + if [ ! -z "$state" ] && [ $state -eq 0 ]; then # no login enabled + hilink_login_enabled=1 + fi + else + hilink_login_enabled="" + fi } # switch mobile data on/off 1/0 -# if SIM is locked, $pin has to be set +# if SIM is locked, $hilink_pin has to be set # parameter: state - ON/OFF or 1/0 function _switchMobileData() { + local mode if [ -z "$1" ]; then return 1; fi _login mode="${1,,}" [ "$mode" = "on" ] && mode=1 [ "$mode" = "off" ] && mode=0 if [[ $mode -ge 0 ]]; then - if _enableSIM "$pin"; then - xmldata="$mode" + if _enableSIM "$hilink_pin"; then + hilink_xmldata="$mode" _sendRequest "api/dialup/mobile-dataswitch" return $? fi @@ -321,31 +337,33 @@ function _switchMobileData() { # parameter: PIN of SIM card function _setPIN() { + local pin if [ -z "$1" ]; then return 1; fi pin="$1" - xmldata="0$pin" + hilink_xmldata="0$pin" _sendRequest "api/pin/operate" return $? } -# Send request to host at http://$host/$apiurl -# data in $xmldata and options in $xtraopts +# Send request to host at http://$hilink_host/$apiurl +# data in $hilink_xmldata and options in $hilink_xtraopts # parameter: apiurl (e.g. "api/user/login") function _sendRequest() { + local ret apiurl status="ERROR" if [ -z "$1" ]; then return 1; fi apiurl="$1" ret=1 - if [ -z "$sessID" ] || [ -z "$token" ]; then _sessToken; fi - if [ -z "$xmldata" ];then - response=$(curl -s http://$host/$apiurl -m 10 \ - -H "Cookie: $sessID") + if [ -z "$hilink_sessID" ] || [ -z "$hilink_token" ]; then _sessToken; fi + if [ -z "$hilink_xmldata" ];then + response=$(curl -s http://$hilink_host/$apiurl -m 10 \ + -H "Cookie: $hilink_sessID") else - response=$(curl -s -X POST http://$host/$apiurl -m 10 \ + response=$(curl -s -X POST http://$hilink_host/$apiurl -m 10 \ -H "Content-Type: text/xml" \ - -H "Cookie: $sessID" \ - -H "__RequestVerificationToken: $token" \ - -d "$xmldata" $xtraopts 2> /dev/null) + -H "Cookie: $hilink_sessID" \ + -H "__RequestVerificationToken: $hilink_token" \ + -d "$hilink_xmldata" $hilink_xtraopts 2> /dev/null) _getToken fi if [ ! -z "$response" ];then @@ -363,28 +381,29 @@ function _sendRequest() { status="ERROR" fi if [[ "$status" =~ ERROR ]]; then _handleError; fi - xtraopts="" - xmldata="" + hilink_xtraopts="" + hilink_xmldata="" return $ret } # handle the list of tokens available after login # parameter: none function _getToken() { - if [ ! -z "$tokenlist" ] && [ ${#tokenlist[@]} -gt 0 ]; then - token=${tokenlist[0]} # get first token in list - tokenlist=("${tokenlist[@]:1}") # remove used token from list - if [ ${#tokenlist[@]} -eq 0 ]; then + if [ ! -z "$hilink_tokenlist" ] && [ ${#hilink_tokenlist[@]} -gt 0 ]; then + hilink_token=${hilink_tokenlist[0]} # get first token in list + hilink_tokenlist=("${hilink_tokenlist[@]:1}") # remove used token from list + if [ ${#hilink_tokenlist[@]} -eq 0 ]; then _logout # use the last token to logout fi - else - _sessToken # old token has been used - need new session + else + _sessToken # old token has been used - need new session fi } # Analyse $status for error code # return error text in $status function _handleError() { + local ret txt txt=$(_getErrorText) if [ -z "$code" ]; then return 1; fi ret=0 @@ -407,20 +426,21 @@ function _handleError() { return "$ret" } -declare -A err_hilink_api -err_hilink_api[101]="Unable to get session ID/token" -err_hilink_api[108001]="Invalid username/password" -err_hilink_api[108002]=${errors[108001]} -err_hilink_api[108006]=${errors[108001]} -err_hilink_api[108003]="User already logged in - need to wait a bit" -err_hilink_api[108007]="Too many login attempts - need to wait a bit" -err_hilink_api[125001]="Invalid session/request token" -err_hilink_api[125002]=${errors[125001]} -err_hilink_api[125003]=${errors[125001]} +declare -A hilink_err_api +hilink_err_api[101]="Unable to get session ID/token" +hilink_err_api[108001]="Invalid username/password" +hilink_err_api[108002]=${hilink_err_api[108001]} +hilink_err_api[108006]=${hilink_err_api[108001]} +hilink_err_api[108003]="User already logged in - need to wait a bit" +hilink_err_api[108007]="Too many login attempts - need to wait a bit" +hilink_err_api[125001]="Invalid session/request token" +hilink_err_api[125002]=${hilink_err_api[125001]} +hilink_err_api[125003]=${hilink_err_api[125001]} # check error and return error text # status passsed in $status, or $1 function _getErrorText() { + local err code errortext err="$status" code="0" if [ ! -z "$1" ]; then err="$1"; fi @@ -428,8 +448,8 @@ function _getErrorText() { errortext="$err" if [[ "$err" =~ ERROR\ *([0-9]*) ]] && [ ! -z "${BASH_REMATCH[1]}" ]; then code=${BASH_REMATCH[1]} - if [ ! -z "$code" ] && [ ! -z "${err_hilink_api[$code]}" ]; then - errortext="${err_hilink_api[$code]}" + if [ ! -z "$code" ] && [ ! -z "${hilink_err_api[$code]}" ]; then + errortext="${hilink_err_api[$code]}" fi fi echo $errortext @@ -437,8 +457,10 @@ function _getErrorText() { } function _hostReachable() { - avail=`timeout 0.5 ping -c 1 $host | sed -rn 's/.*time=.*/1/p'` - if [ -z "$avail" ]; then return 1; fi + local avail + avail=$( timeout 0.5 ping -c 1 $hilink_host | sed -rn 's/.*time=.*/1/p' ) + if [ -z "$avail" ]; then status="ERROR: Not reachable"; return 1; fi + status="OK" return 0; } @@ -446,6 +468,7 @@ function _hostReachable() { # call another function first! # parameter: tag-name function _valueFromResponse() { + local par value if [ -z "$response" ] || [ -z "$1" ]; then return 1; fi par="$1" value=$(echo $response | sed -rn 's/.*<'$par'>(.*)<\/'$par'>.*/\1/pi') @@ -468,15 +491,15 @@ function _keyValuePairs() { return 0 } -host=$host_default -user="admin" -pw="" -token="" -tokenlist="" -sessID="" -xmldata="" -xtraopts="" +hilink_token="" +hilink_tokenlist="" +hilink_sessID="" +hilink_xmldata="" +hilink_xtraopts="" +hilink_host=$hilink_host_default +hilink_user="admin" +hilink_password="" +hilink_pin="" response="" status="" -pwtype=-1 - + \ No newline at end of file diff --git a/config/client_config/info_huawei_hilink.sh b/config/client_config/info_huawei_hilink.sh index 32b279546..c98ee9fe6 100644 --- a/config/client_config/info_huawei_hilink.sh +++ b/config/client_config/info_huawei_hilink.sh @@ -14,10 +14,10 @@ # zbchristian 2021 function _setAPIParams() { - if [ ! -z "$hostip" ]; then host="$hostip"; fi - if [ ! -z "$username" ]; then user="$username"; fi - if [ ! -z "$password" ]; then pw="$password"; fi - if [ ! -z "$simpin" ]; then pin="$simpin"; fi + if [ ! -z "$hostip" ]; then hilink_host="$hostip"; fi + if [ ! -z "$username" ]; then hilink_user="$username"; fi + if [ ! -z "$password" ]; then hilink_password="$password"; fi + if [ ! -z "$simpin" ]; then hilink_pin="$simpin"; fi } if [ -z "$1" ]; then echo "none"; exit; fi @@ -44,7 +44,7 @@ if [ "$opt" = "connected" ]; then result=$(_getMobileDataStatus) _closeHilinkAPI else - info_file="/tmp/huawei_infos_${hostip}_${id -u}.dat" + info_file="/tmp/huawei_infos_${hostip}_$(id -u).dat" if [ -f "$info_file" ]; then age=$(( $(date +%s) - $(stat $info_file -c %Y) )) if [[ $age -gt 10 ]]; then rm -f $info_file; fi @@ -75,7 +75,7 @@ else key="msisdn" ;; imei|imsi|rssi|rsrq|rsrp|sinr|ecio) - key="$opt" + key="$property" ;; signal) key="rsrq" diff --git a/config/client_config/onoff_huawei_hilink.sh b/config/client_config/onoff_huawei_hilink.sh index 2ee413d25..cd050016b 100644 --- a/config/client_config/onoff_huawei_hilink.sh +++ b/config/client_config/onoff_huawei_hilink.sh @@ -5,6 +5,7 @@ # options: -u, --user - user name (default "admin") # -P, --password - password # -h, --host - host ip address (default 192.168.8.1) +# -d, --devname - device name (IP is extracted using default route) # -p, --pin - PIN of SIM card # -c, --connect - connect 0/1 to set datamode off/on # @@ -12,24 +13,37 @@ # # zbchristian 2021 -# include the hilink API (defaults: user=admin, host=192.168.8.1) +# include the hilink API (defaults: hilink_user=admin, hilink_host=192.168.8.1) source /usr/local/sbin/huawei_hilink_api.sh +# include the raspap helper functions +source /usr/local/sbin/raspap_helpers.sh + datamode="" +devname="" while [ -n "$1" ]; do case "$1" in - -u|--user) user="$2"; shift ;; - -P|--password) pw="$2"; shift ;; - -p|--pin) if [[ $2 =~ ^[0-9]{4,8} ]]; then pin="$2"; fi; shift ;; - -h|--host) host="$2"; shift ;; + -u|--user) hilink_user="$2"; shift ;; + -P|--password) hilink_password="$2"; shift ;; + -p|--pin) if [[ $2 =~ ^[0-9]{4,8} ]]; then hilink_pin="$2"; fi; shift ;; + -h|--host) hilink_host="$2"; shift ;; + -d|--devname) devname="$2"; shift ;; -c|--connect) if [ "$2" = "1" ]; then datamode=1; else datamode=0; fi; shift ;; esac shift done -echo "Hilink: switch device at $host to mode $datamode" | systemd-cat +if [ ! _loginState ] && [ -z "$hilink_password" ] || [ -z "$hilink_pin" ]; then _getAuthRouter; fi + +if [ ! -z "$devname" ]; then # get host IP for given device name + gw=$(ip route list | sed -rn "s/default via (([0-9]{1,3}\.){3}[0-9]{1,3}).*dev $devname.*/\1/p") + if [ -z "$gw" ]; then exit; fi # device name not found in routing list -> abort + hilink_host="$gw" +fi + +echo "Hilink: switch device at $hilink_host to mode $datamode" | systemd-cat -status="usage: -c 1/0 to disconnect/disconnect" +status="usage: -c 1/0 to disconnect/connect" if [ -z "$datamode" ] || [ ! _initHilinkAPI ]; then echo "Hilink: failed - return status: $status"; exit; fi if ! _switchMobileData "$datamode"; then echo -n "Hilink: could not switch the data mode on/off . Error: ";_getErrorText; fi diff --git a/config/client_config/raspap_helpers.sh b/config/client_config/raspap_helpers.sh new file mode 100644 index 000000000..a20ba3097 --- /dev/null +++ b/config/client_config/raspap_helpers.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# +# Helper functions to extract informations from RaspAP config/settings +# +# zbchristian 2021 +# +# get the values of a RaspAP config variable +# call: _getRaspapConfig RASPAP_MOBILEDATA_CONFIG + +raspap_webroot="/var/www/html" + +function _getWebRoot() { + local path + path=$(cat /etc/lighttpd/lighttpd.conf | sed -rn "s/server.document-root \s*= \"([^ \s]*)\"/\1/p") + if [ ! -z "$path" ]; then raspap_webroot="$path"; fi + if [ -z "$path" ]; then return 1; else return 0; fi +} + +# expand an RaspAP config variable utilizing PHP +function _getRaspapConfig() { + local conf var + raspap_config="" + var="$1" + if [ ! -z "$var" ]; then + if ! _getWebRoot; then return 1; fi + conf="$raspap_webroot/includes/config.php" + if [ -f "$conf" ]; then + conf=$(php -r 'include "'$conf'"; echo '$var';' 2> /dev/null) + if [ ! -z "$conf" ] && [ -d ${conf%/*} ]; then raspap_config="$conf"; fi + fi + fi + if [ -z "$raspap_config" ]; then return 1; else return 0; fi +} + +# Username and password for mobile data devices is stored in a file (RASPAP_MOBILEDATA_CONFIG) +function _getAuthRouter() { + local mfile mdata pin user pw + if ! _getRaspapConfig "RASPI_MOBILEDATA_CONFIG"; then return 1; fi + mfile="$raspap_config" + if [ -f $mfile ]; then + mdata=$(cat "$mfile") + pin=$(echo "$mdata" | sed -rn 's/pin = ([^ \s]*)/\1/ip') + if [ ! -z "$pin" ]; then raspap_pin="$pin"; fi + user=$(echo "$mdata" | sed -rn 's/router_user = ([^ \s]*)/\1/ip') + if [ ! -z "$user" ]; then raspap_user="$user"; fi + pw=$(echo "$mdata" | sed -rn 's/router_pw = ([^ \s]*)/\1/ip') + if [ ! -z "$pw" ]; then raspap_password="$pw"; fi + return 0 + fi + return 1 +} diff --git a/config/client_config/start_huawei_hilink.service b/config/client_config/start_huawei_hilink@.service similarity index 76% rename from config/client_config/start_huawei_hilink.service rename to config/client_config/start_huawei_hilink@.service index 19d775d06..b735b1b1a 100644 --- a/config/client_config/start_huawei_hilink.service +++ b/config/client_config/start_huawei_hilink@.service @@ -5,7 +5,7 @@ Description=Bring up HUAWEI mobile hilink device Type=oneshot RemainAfterExit=no ExecStart=/bin/sleep 15 -ExecStart=/usr/local/sbin/switchClientState.sh up +ExecStart=/usr/local/sbin/onoff_huawei_hilink.sh -c 1 -d %i [Install] Alias=start_ltemodem.service diff --git a/config/client_config/switchClientState.sh b/config/client_config/switchClientState.sh deleted file mode 100644 index acdd28928..000000000 --- a/config/client_config/switchClientState.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -# start with "sudo" -# parameters: up or on -# -# switch client state to UP -# the actual code is in PHP - -# get webroot -webroot=$(cat /etc/lighttpd/lighttpd.conf | sed -rn 's/server.document-root\s*=\s*\"(.*)\"\s*$/\1/p') -webuser=$(cat /etc/lighttpd/lighttpd.conf | sed -rn 's/server.username\s*=\s*\"(.*)\"\s*$/\1/p') -if [ -z "$webroot" ] || [ ! -d "$webroot" ] || [ -z "$webuser" ]; then - echo "$0 : Problem to obtain webroot directory and/or web user - exit" | systemd-cat - exit -fi -cd $webroot - -state="" -if [ ! -z $1 ] && [[ $1 =~ ^(up|on|UP|ON)$ ]]; then - state="up" -elif [ ! -z $1 ] && [[ $1 =~ ^(down|off|DOWN|OFF)$ ]]; then - state="down" -fi - -[ -z "$state" ] && exit - -sudo -u $webuser php << _EOF_ - -_EOF_ - - diff --git a/config/client_udev_prototypes.json b/config/client_udev_prototypes.json index a147e776e..bd49f4197 100644 --- a/config/client_udev_prototypes.json +++ b/config/client_udev_prototypes.json @@ -39,10 +39,10 @@ "type": "hilink", "type_info": "Huawei Hilink", "clientid": 4, - "comment": "Huawei mobile data device in router mode. Control via HTTP", + "comment": "Huawei mobile data device in router mode. Control via HTTP. Device is connecting via service", "name_prefix": "hilink", "default_ip": "192.168.8.1", - "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"$IDVENDOR$\", ATTRS{idProduct}==\"$IDPRODUCT$\", NAME=\"$DEVNAME$\", ENV{raspapType}=\"hilink\", TAG+=\"systemd\", ENV{SYSTEMD_WANTS}=\"start start_huawei_hilink.service\" " + "udev_rule": "SUBSYSTEM==\"net\", ACTION==\"add\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"$IDVENDOR$\", ATTRS{idProduct}==\"$IDPRODUCT$\", NAME=\"$DEVNAME$\", ENV{raspapType}=\"hilink\", TAG+=\"systemd\", ENV{SYSTEMD_WANTS}=\"start start_huawei_hilink@hilink%n.service\" " }, { "type": "phone", diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index 355fd8eb9..fc692e1c7 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -53,7 +53,6 @@ www-data ALL=(ALL) NOPASSWD:/usr/local/sbin/onoff_huawei_hilink.sh * 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 From 8f702a2a559fa22eea9859465146e330e20106a0 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Fri, 18 Jun 2021 17:27:11 +0200 Subject: [PATCH 210/300] Correct Hilink authentication and service --- config/client_config/80-raspap-net-devices.rules | 2 +- config/client_config/onoff_huawei_hilink.sh | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/config/client_config/80-raspap-net-devices.rules b/config/client_config/80-raspap-net-devices.rules index 33e6584c3..ddec82037 100644 --- a/config/client_config/80-raspap-net-devices.rules +++ b/config/client_config/80-raspap-net-devices.rules @@ -1,3 +1,3 @@ -SUBSYSTEM=="net", ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="14db", NAME="hilink%n", TAG+="systemd", ENV{SYSTEMD_WANTS}="start start_huawei_hilink.service" +SUBSYSTEM=="net", ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="14db", NAME="hilink%n", TAG+="systemd", ENV{SYSTEMD_WANTS}="start start_huawei_hilink@hilink%n.service" diff --git a/config/client_config/onoff_huawei_hilink.sh b/config/client_config/onoff_huawei_hilink.sh index cd050016b..9b3f8413c 100644 --- a/config/client_config/onoff_huawei_hilink.sh +++ b/config/client_config/onoff_huawei_hilink.sh @@ -33,14 +33,19 @@ while [ -n "$1" ]; do shift done -if [ ! _loginState ] && [ -z "$hilink_password" ] || [ -z "$hilink_pin" ]; then _getAuthRouter; fi - if [ ! -z "$devname" ]; then # get host IP for given device name gw=$(ip route list | sed -rn "s/default via (([0-9]{1,3}\.){3}[0-9]{1,3}).*dev $devname.*/\1/p") if [ -z "$gw" ]; then exit; fi # device name not found in routing list -> abort hilink_host="$gw" fi +if [ -z "$hilink_password" ] || [ -z "$hilink_pin" ]; then + _getAuthRouter + if [ ! -z "$raspap_user" ]; then hilink_user="$raspap_user"; fi + if [ ! -z "$raspap_password" ]; then hilink_password="$raspap_password"; fi + if [ ! -z "$raspap_pin" ]; then hilink_pin="$raspap_pin"; fi +fi + echo "Hilink: switch device at $hilink_host to mode $datamode" | systemd-cat status="usage: -c 1/0 to disconnect/connect" From ab9e9ae8d4dd0540b3114a4ca5804aad63bcc7fa Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Sun, 20 Jun 2021 17:10:48 +0200 Subject: [PATCH 211/300] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cf2d990e6..b371b9b18 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,13 @@ As part of the initial rollout of Insiders, all previous one-time backers of Ras ## Exclusive features 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](https://docs.raspap.com/openvpn/#multiple-client-configs) -✅ [OpenVPN certificate authentication](https://docs.raspap.com/openvpn/#certificate-authentication) +✅ [Manage OpenVPN client configs](https://docs.raspap.com/openvpn.html#multiple-client-configs) +✅ [OpenVPN certificate authentication](https://docs.raspap.com/openvpn.html#certificate-authentication) ✅ OpenVPN service logging ✅ Night mode toggle ✅ Restrict network to static clients -✅ [WireGuard support](https://docs.raspap.com/wireguard/) -✅ [Set AP transmit power](https://docs.raspap.com/ap-basics/#transmit-power) +✅ [WireGuard support](https://docs.raspap.com/wireguard.html) +✅ [Set AP transmit power](https://docs.raspap.com/ap-basics.html#transmit-power) ✅ Mobile data client support ⚙️ Traffic shaping (in progress) From 8ae0fce366f287a33a6b18bf8e747ff08c121843 Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 3 Jul 2021 23:01:35 +0100 Subject: [PATCH 212/300] Initial commit --- app/lib/uploader.php | 503 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 503 insertions(+) create mode 100644 app/lib/uploader.php diff --git a/app/lib/uploader.php b/app/lib/uploader.php new file mode 100644 index 000000000..e8a31b8e1 --- /dev/null +++ b/app/lib/uploader.php @@ -0,0 +1,503 @@ + + * @author Aivis Silins + * @link https://github.com/aivis/PHP-file-upload-class + * @license https://github.com/raspap/raspap-webgui/blob/master/LICENSE + */ + +class Upload +{ + + /** + * Default directory persmissions (destination) + */ + protected $default_permissions = 0750; + + /** + * File post array + * + * @var array + */ + protected $file_post = array(); + + /** + * Destination directory + * + * @var string + */ + protected $destination; + + /** + * Fileinfo + * + * @var object + */ + protected $finfo; + + /** + * Data about file + * + * @var array + */ + public $file = array(); + + /** + * Max. file size + * + * @var int + */ + protected $max_file_size; + + /** + * Allowed mime types + * + * @var array + */ + protected $mimes = array(); + + /** + * Temp path + * + * @var string + */ + protected $tmp_name; + + /** + * Validation errors + * + * @var array + */ + protected $validation_errors = array(); + + /** + * Filename (new) + * + * @var string + */ + protected $filename; + + /** + * Internal callbacks (filesize check, mime, etc) + * + * @var array + */ + private $callbacks = array(); + + /** + * Root dir + * + * @var string + */ + protected $root; + + /** + * Return upload object + * + * $destination = 'path/to/file/destination/'; + * + * @param string $destination + * @param string $root + * @return Upload + */ + public static function factory($destination, $root = false) + { + return new Upload($destination, $root); + } + + /** + * Define root constant and set & create destination path + * + * @param string $destination + * @param string $root + */ + public function __construct($destination, $root = false) + { + if ($root) { + $this->root = $root; + } else { + $this->root = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR; + } + + // set & create destination path + if (!$this->set_destination($destination)) { + throw new Exception('Upload: Unable to create destination. '.$this->root . $this->destination); + } + //create finfo object + $this->finfo = new finfo(); + } + + /** + * Set target filename + * + * @param string $filename + */ + public function set_filename($filename) + { + $this->filename = $filename; + } + + /** + * Check & Save file + * + * Return data about current upload + * + * @return array + */ + public function upload($filename = false) + { + if($filename ) { + $this->set_filename($filename); + } + + $this->set_filename($filename); + + if ($this->check()) { + $this->save(); + } + + // return state data + return $this->get_state(); + } + + /** + * Save file on server + * Return state data + * + * @return array + */ + public function save() + { + $this->save_file(); + return $this->get_state(); + } + + /** + * Validate file (execute callbacks) + * Returns TRUE if validation successful + * + * @return bool + */ + public function check() + { + //execute callbacks (check filesize, mime, also external callbacks + $this->validate(); + + //add error messages + $this->file['errors'] = $this->get_errors(); + + //change file validation status + $this->file['status'] = empty($this->validation_errors); + + return $this->file['status']; + } + + /** + * Get current state data + * + * @return array + */ + public function get_state() + { + return $this->file; + } + + /** + * Save file on server + */ + protected function save_file() + { + //create & set new filename + if(empty($this->filename)) { + $this->create_new_filename(); + } + + //set filename + $this->file['filename'] = $this->filename; + + //set full path + $this->file['full_path'] = $this->root . $this->destination . $this->filename; + $this->file['path'] = $this->destination . $this->filename; + + $status = move_uploaded_file($this->tmp_name, $this->file['full_path']); + + //checks whether upload successful + if (!$status) { + throw new Exception('Upload: Failed to upload file.'); + } + + //done + $this->file['status'] = true; + } + + /** + * Set data about file + */ + protected function set_file_data() + { + $file_size = $this->get_file_size(); + $this->file = array( + 'status' => false, + 'destination' => $this->destination, + 'size_in_bytes' => $file_size, + 'size_in_mb' => $this->bytes_to_mb($file_size), + 'mime' => $this->get_file_mime(), + 'filename' => $this->file_post['name'], + 'tmp_name' => $this->file_post['tmp_name'], + 'post_data' => $this->file_post, + ); + } + + /** + * Set validation error + * + * @param string $message + */ + public function set_error($message) + { + $this->validation_errors[] = $message; + } + + /** + * Return validation errors + * + * @return array + */ + public function get_errors() + { + return $this->validation_errors; + } + + /** + * Set external callback methods + * + * @param object $instance_of_callback_object + * @param array $callback_methods + */ + public function callbacks($instance_of_callback_object, $callback_methods) + { + if (empty($instance_of_callback_object)) { + throw new Exception('Upload: $instance_of_callback_object cannot be empty.'); + + } + + if (!is_array($callback_methods)) { + throw new Exception('Upload: $callback_methods data type need to be array.'); + } + + $this->external_callback_object = $instance_of_callback_object; + $this->external_callback_methods = $callback_methods; + } + + /** + * Execute callbacks + */ + protected function validate() + { + //get curent errors + $errors = $this->get_errors(); + + if (empty($errors)) { + + //set data about current file + $this->set_file_data(); + + //execute internal callbacks + $this->execute_callbacks($this->callbacks, $this); + + //execute external callbacks + $this->execute_callbacks($this->external_callback_methods, $this->external_callback_object); + } + } + + /** + * Execute callbacks + */ + protected function execute_callbacks($callbacks, $object) + { + foreach($callbacks as $method) { + $object->$method($this); + + } + } + + /** + * File mime type validation callback + * + * @param object $object + */ + protected function check_mime_type($object) + { + if (!empty($object->mimes)) { + if (!in_array($object->file['mime'], $object->mimes)) { + $object->set_error('Mime type not allowed.'); + } + } + } + + /** + * Set allowed mime types + * + * @param array $mimes + */ + public function set_allowed_mime_types($mimes) + { + $this->mimes = $mimes; + //if mime types is set -> set callback + $this->callbacks[] = 'check_mime_type'; + } + + /** + * File size validation callback + * + * @param object $object + */ + protected function check_file_size($object) + { + if (!empty($object->max_file_size)) { + $file_size_in_mb = $this->bytes_to_mb($object->file['size_in_bytes']); + if ($object->max_file_size <= $file_size_in_mb) { + $object->set_error('File exceeds maximum allowed size.'); + } + } + } + + /** + * Set max file size + * + * @param int $size + */ + public function set_max_file_size($size) + { + $this->max_file_size = $size; + + //if max file size is set -> set callback + $this->callbacks[] = 'check_file_size'; + } + + /** + * Set File array to object + * + * @param array $file + */ + public function file($file) + { + $this->set_file_array($file); + } + + /** + * Set file array + * + * @param array $file + */ + protected function set_file_array($file) + { + //checks whether file array is valid + if (!$this->check_file_array($file)) { + //file not selected or some bigger problems (broken files array) + $this->set_error('Please select file.'); + } + + //set file data + $this->file_post = $file; + + //set tmp path + $this->tmp_name = $file['tmp_name']; + } + + /** + * Checks whether Files post array is valid + * + * @return bool + */ + protected function check_file_array($file) + { + return isset($file['error']) + && !empty($file['name']) + && !empty($file['type']) + && !empty($file['tmp_name']) + && !empty($file['size']); + } + + /** + * Get file mime type + * + * @return string + */ + protected function get_file_mime() + { + return $this->finfo->file($this->tmp_name, FILEINFO_MIME_TYPE); + } + + /** + * Get file size + * + * @return int + */ + protected function get_file_size() + { + return filesize($this->tmp_name); + } + + /** + * Set destination path (return TRUE on success) + * + * @param string $destination + * @return bool + */ + protected function set_destination($destination) + { + $this->destination = $destination . DIRECTORY_SEPARATOR; + return $this->destination_exist() ? true : $this->create_destination(); + } + + /** + * Checks whether destination folder exists + * + * @return bool + */ + protected function destination_exist() + { + return is_writable($this->root . $this->destination); + } + + /** + * Create path to destination + * + * @param string $dir + * @return bool + */ + protected function create_destination() + { + return mkdir($this->root . $this->destination, $this->default_permissions, true); + } + + /** + * Set unique filename + * + * @return string + */ + protected function create_new_filename() + { + $filename = sha1(mt_rand(1, 9999) . $this->destination . uniqid()) . time(); + $this->set_filename($filename); + } + + /** + * Convert bytes to MB + * + * @param int $bytes + * @return int + */ + protected function bytes_to_mb($bytes) + { + return round(($bytes / 1048576), 2); + } +} + From b7a9c6254edfe88d23f2ebc07f2ae843d582a7b8 Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 3 Jul 2021 23:03:14 +0100 Subject: [PATCH 213/300] Refactor w/ file upload class --- includes/openvpn.php | 78 ++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 50 deletions(-) diff --git a/includes/openvpn.php b/includes/openvpn.php index 67084df4f..6243b603b 100755 --- a/includes/openvpn.php +++ b/includes/openvpn.php @@ -3,6 +3,7 @@ require_once 'includes/status_messages.php'; require_once 'includes/config.php'; require_once 'includes/wifi_functions.php'; +require_once 'app/lib/uploader.php'; getWifiInterface(); @@ -87,6 +88,18 @@ function DisplayOpenVPNConfig() ); } +/* File upload callback object + * + */ +class validation { + public function check_name_length($object) + { + if (strlen($object->file['filename']) > 255) { + $object->set_error('File name is too long.'); + } + } +} + /** * Validates uploaded .ovpn file, adds auth-user-pass and * stores auth credentials in login.conf. Copies files from @@ -100,8 +113,10 @@ function DisplayOpenVPNConfig() */ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword) { - $tmp_ovpnclient = '/tmp/ovpnclient.ovpn'; - $tmp_authdata = '/tmp/authdata'; + define('KB', 1024); + $tmp_destdir = '/tmp/'; + $tmp_ovpnclient = $tmp_destdir .'ovpn/ovpnclient.ovpn'; + $tmp_authdata = $tmp_destdir .'ovpn/authdata'; $auth_flag = 0; try { @@ -110,61 +125,24 @@ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword) throw new RuntimeException('Invalid parameters'); } - // Parse returned errors - switch ($file['error']) { - case UPLOAD_ERR_OK: - break; - case UPLOAD_ERR_NO_FILE: - throw new RuntimeException('OpenVPN configuration file not sent'); - case UPLOAD_ERR_INI_SIZE: - case UPLOAD_ERR_FORM_SIZE: - throw new RuntimeException('Exceeded filesize limit'); - default: - throw new RuntimeException('Unknown errors'); - } - - // Validate extension - $ext = pathinfo($file['name'], PATHINFO_EXTENSION); - if ($ext != 'ovpn') { - throw new RuntimeException('Invalid file extension'); - } + $upload = Upload::factory('ovpn',$tmp_destdir); + $upload->set_max_file_size(64*KB); + $upload->set_allowed_mime_types(array('ovpn' => 'text/plain')); + $upload->file($file); - // Validate MIME type - $finfo = new finfo(FILEINFO_MIME_TYPE); - if (false === $ext = array_search( - $finfo->file($file['tmp_name']), - array( - 'ovpn' => 'text/plain' - ), - true - ) - ) { - throw new RuntimeException('Invalid file format'); - } + $validation = new validation; + $upload->callbacks($validation, array('check_name_length')); + $results = $upload->upload(); - // Validate filesize - define('KB', 1024); - if ($file['size'] > 64*KB) { - throw new RuntimeException('File size limit exceeded'); - } - - // Use safe filename, save to /tmp - if (!move_uploaded_file( - $file['tmp_name'], - sprintf( - '/tmp/%s.%s', - 'ovpnclient', - $ext - ) - ) - ) { - throw new RuntimeException('Unable to move uploaded file'); + if (!empty($results['errors'])) { + throw new RuntimeException($results['errors'][0]); } + echo '

' . var_export($results, true) . '
'; + #die(); // Good file upload, update auth credentials if present if (!empty($authUser) && !empty($authPassword)) { $auth_flag = 1; - // Move tmp authdata to /etc/openvpn/login.conf $auth.= $authUser .PHP_EOL . $authPassword .PHP_EOL; file_put_contents($tmp_authdata, $auth); chmod($tmp_authdata, 0644); From 8409c3e7d89a0d68bd2646cb03eb268504923ada Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 4 Jul 2021 10:46:00 +0100 Subject: [PATCH 214/300] Update sudoers ovpn actions --- installers/raspap.sudoers | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index 260a45fc3..2892c2275 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -20,8 +20,7 @@ www-data ALL=(ALL) NOPASSWD:/bin/systemctl start openvpn-client@client www-data ALL=(ALL) NOPASSWD:/bin/systemctl enable openvpn-client@client www-data ALL=(ALL) NOPASSWD:/bin/systemctl stop openvpn-client@client www-data ALL=(ALL) NOPASSWD:/bin/systemctl disable openvpn-client@client -www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/ovpnclient.ovpn /etc/openvpn/client/*.conf -www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/authdata /etc/openvpn/client/*.conf +www-data ALL=(ALL) NOPASSWD:/bin/mv /tmp/ovpn/* /etc/openvpn/client/*.conf www-data ALL=(ALL) NOPASSWD:/usr/bin/ln -s /etc/openvpn/client/*.conf /etc/openvpn/client/*.conf www-data ALL=(ALL) NOPASSWD:/bin/rm /etc/openvpn/client/*.conf www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/dnsmasqdata /etc/dnsmasq.d/090_*.conf From 699f9ff3970666ef297471f7161269b234ca4223 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 4 Jul 2021 10:47:45 +0100 Subject: [PATCH 215/300] Bugfix + remove debug output --- includes/openvpn.php | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/includes/openvpn.php b/includes/openvpn.php index 6243b603b..7d4b28588 100755 --- a/includes/openvpn.php +++ b/includes/openvpn.php @@ -88,18 +88,6 @@ function DisplayOpenVPNConfig() ); } -/* File upload callback object - * - */ -class validation { - public function check_name_length($object) - { - if (strlen($object->file['filename']) > 255) { - $object->set_error('File name is too long.'); - } - } -} - /** * Validates uploaded .ovpn file, adds auth-user-pass and * stores auth credentials in login.conf. Copies files from @@ -115,8 +103,6 @@ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword) { define('KB', 1024); $tmp_destdir = '/tmp/'; - $tmp_ovpnclient = $tmp_destdir .'ovpn/ovpnclient.ovpn'; - $tmp_authdata = $tmp_destdir .'ovpn/authdata'; $auth_flag = 0; try { @@ -137,17 +123,16 @@ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword) if (!empty($results['errors'])) { throw new RuntimeException($results['errors'][0]); } - echo '
' . var_export($results, true) . '
'; - #die(); // Good file upload, update auth credentials if present if (!empty($authUser) && !empty($authPassword)) { $auth_flag = 1; + $tmp_authdata = $tmp_destdir .'ovpn/authdata'; $auth.= $authUser .PHP_EOL . $authPassword .PHP_EOL; file_put_contents($tmp_authdata, $auth); chmod($tmp_authdata, 0644); $client_auth = RASPI_OPENVPN_CLIENT_PATH.pathinfo($file['name'], PATHINFO_FILENAME).'_login.conf'; - system("sudo cp $tmp_authdata $client_auth", $return); + system("sudo mv $tmp_authdata $client_auth", $return); system("sudo rm ".RASPI_OPENVPN_CLIENT_LOGIN, $return); system("sudo ln -s $client_auth ".RASPI_OPENVPN_CLIENT_LOGIN, $return); if ($return !=0) { @@ -161,9 +146,11 @@ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword) $status->addMessage($line, 'info'); } + // Move uploaded ovpn config from /tmp and create symlink $client_ovpn = RASPI_OPENVPN_CLIENT_PATH.pathinfo($file['name'], PATHINFO_FILENAME).'_client.conf'; + $tmp_ovpn = $results['full_path']; chmod($tmp_ovpnclient, 0644); - system("sudo cp $tmp_ovpnclient $client_ovpn", $return); + system("sudo mv $tmp_ovpn $client_ovpn", $return); system("sudo rm ".RASPI_OPENVPN_CLIENT_CONFIG, $return); system("sudo ln -s $client_ovpn ".RASPI_OPENVPN_CLIENT_CONFIG, $return); @@ -179,3 +166,16 @@ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword) return $status; } } + +/* File upload callback object + * + */ +class validation { + public function check_name_length($object) + { + if (strlen($object->file['filename']) > 255) { + $object->set_error('File name is too long.'); + } + } +} + From 87352b8b42d88123d430e1fdd2d68fd128afba9f Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 4 Jul 2021 11:15:50 +0100 Subject: [PATCH 216/300] Update w/ namespace, fix configauth for client.conf --- includes/openvpn.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/includes/openvpn.php b/includes/openvpn.php index 7d4b28588..598f0b030 100755 --- a/includes/openvpn.php +++ b/includes/openvpn.php @@ -111,7 +111,7 @@ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword) throw new RuntimeException('Invalid parameters'); } - $upload = Upload::factory('ovpn',$tmp_destdir); + $upload = \RaspAP\Uploader\Upload::factory('ovpn',$tmp_destdir); $upload->set_max_file_size(64*KB); $upload->set_allowed_mime_types(array('ovpn' => 'text/plain')); $upload->file($file); @@ -128,7 +128,7 @@ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword) if (!empty($authUser) && !empty($authPassword)) { $auth_flag = 1; $tmp_authdata = $tmp_destdir .'ovpn/authdata'; - $auth.= $authUser .PHP_EOL . $authPassword .PHP_EOL; + $auth = $authUser .PHP_EOL . $authPassword .PHP_EOL; file_put_contents($tmp_authdata, $auth); chmod($tmp_authdata, 0644); $client_auth = RASPI_OPENVPN_CLIENT_PATH.pathinfo($file['name'], PATHINFO_FILENAME).'_login.conf'; @@ -141,15 +141,15 @@ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword) } // Set iptables rules and, optionally, auth-user-pass - exec("sudo /etc/raspap/openvpn/configauth.sh $tmp_ovpnclient $auth_flag " .$_SESSION['ap_interface'], $return); + $tmp_ovpn = $results['full_path']; + exec("sudo /etc/raspap/openvpn/configauth.sh $tmp_ovpn $auth_flag " .$_SESSION['ap_interface'], $return); foreach ($return as $line) { $status->addMessage($line, 'info'); } // Move uploaded ovpn config from /tmp and create symlink $client_ovpn = RASPI_OPENVPN_CLIENT_PATH.pathinfo($file['name'], PATHINFO_FILENAME).'_client.conf'; - $tmp_ovpn = $results['full_path']; - chmod($tmp_ovpnclient, 0644); + chmod($tmp_ovpn, 0644); system("sudo mv $tmp_ovpn $client_ovpn", $return); system("sudo rm ".RASPI_OPENVPN_CLIENT_CONFIG, $return); system("sudo ln -s $client_ovpn ".RASPI_OPENVPN_CLIENT_CONFIG, $return); From 8cd2c59ca12a1d4845ed40871813eb27cf535be1 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 4 Jul 2021 11:16:21 +0100 Subject: [PATCH 217/300] Update w/ namespace --- app/lib/uploader.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/lib/uploader.php b/app/lib/uploader.php index e8a31b8e1..6e19d9e84 100644 --- a/app/lib/uploader.php +++ b/app/lib/uploader.php @@ -12,6 +12,8 @@ * @license https://github.com/raspap/raspap-webgui/blob/master/LICENSE */ +namespace RaspAP\Uploader; + class Upload { @@ -130,7 +132,7 @@ public function __construct($destination, $root = false) throw new Exception('Upload: Unable to create destination. '.$this->root . $this->destination); } //create finfo object - $this->finfo = new finfo(); + $this->finfo = new \finfo(); } /** From de586e00249701fdd20f8d3d5f72b127271cc001 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 6 Jul 2021 22:18:43 +0100 Subject: [PATCH 218/300] Work in progress: WG server config panels --- app/js/custom.js | 11 ++++- templates/wg/general.php | 96 ++++++++++++++++++++++++++-------------- 2 files changed, 73 insertions(+), 34 deletions(-) diff --git a/app/js/custom.js b/app/js/custom.js index 4ba0436b0..ff0b303f5 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -307,7 +307,6 @@ $('#ovpn-confirm-activate').on('shown.bs.modal', function (e) { }); $('#ovpn-userpw,#ovpn-certs').on('click', function (e) { -// e.stopPropagation(); if (this.id == 'ovpn-userpw') { $('#PanelCerts').hide(); $('#PanelUserPW').show(); @@ -317,6 +316,16 @@ $('#ovpn-userpw,#ovpn-certs').on('click', function (e) { } }); +$('#wg-upload,#wg-manual').on('click', function (e) { + if (this.id == 'wg-upload') { + $('#PanelManual').hide(); + $('#PanelUpload').show(); + } else if (this.id == 'wg-manual') { + $('#PanelUpload').hide(); + $('#PanelManual').show(); + } +}); + // Add the following code if you want the name of the file appear on select $(".custom-file-input").on("change", function() { var fileName = $(this).val().split("\\").pop(); diff --git a/templates/wg/general.php b/templates/wg/general.php index 06d098112..86eefe752 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -9,47 +9,77 @@

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

- -
-
- -
-
- -
- - -
-
+
+
+ +
- -
-
- - -
+
+ +
-
-
- - -
-
+
+
+
+
+
+

+ wg0.conf WireGuard configuration on this device.") ?> +

+
+
+ +
+ +
+ + +
+
+
-
-
- - -
-
+
+ + +
+
+ + +
+ +
+ + +
+
+
+ +
+
+
+

+ .conf file to this device.") ?> +

+
+
+
+
+
+ + +
+
+
+
+
+
-
+
From 84d5584150b556abbe03dbab57624da0b0562525 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 6 Jul 2021 23:10:10 +0100 Subject: [PATCH 219/300] Move file upload validation class to functions --- includes/functions.php | 25 +++++++++++++++++++++---- includes/openvpn.php | 12 ------------ 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/includes/functions.php b/includes/functions.php index 93c609a35..b30ff8257 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -721,13 +721,15 @@ function validateCidr($cidr) } // Validates a host or FQDN -function validate_host($host) { +function validate_host($host) +{ return preg_match('/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i', $host); } // Gets night mode toggle value // @return boolean -function getNightmode(){ +function getNightmode() +{ if ($_COOKIE['theme'] == 'lightsout.css') { return true; } else { @@ -736,7 +738,8 @@ function getNightmode(){ } // search array for matching string and return only first matching group -function preg_only_match($pat,$haystack) { +function preg_only_match($pat,$haystack) +{ $match = ""; if(!empty($haystack) && !empty($pat)) { if(!is_array($haystack)) $haystack = array($haystack); @@ -754,10 +757,24 @@ function qr_encode($str) return preg_replace('/(?file['filename']) > 255) { + $object->set_error('File name is too long.'); + } + } +} + diff --git a/includes/openvpn.php b/includes/openvpn.php index 598f0b030..eae9c42d8 100755 --- a/includes/openvpn.php +++ b/includes/openvpn.php @@ -167,15 +167,3 @@ function SaveOpenVPNConfig($status, $file, $authUser, $authPassword) } } -/* File upload callback object - * - */ -class validation { - public function check_name_length($object) - { - if (strlen($object->file['filename']) > 255) { - $object->set_error('File name is too long.'); - } - } -} - From 1adaca1ea1cba242efa60cdd8963554339477650 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 6 Jul 2021 23:11:16 +0100 Subject: [PATCH 220/300] Set file upload as default wg config method --- app/js/custom.js | 4 ++++ templates/wg/general.php | 51 ++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/app/js/custom.js b/app/js/custom.js index ff0b303f5..dc886d076 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -316,6 +316,10 @@ $('#ovpn-userpw,#ovpn-certs').on('click', function (e) { } }); +$(document).ready(function(){ + $("#PanelManual").hide(); +}); + $('#wg-upload,#wg-manual').on('click', function (e) { if (this.id == 'wg-upload') { $('#PanelManual').hide(); diff --git a/templates/wg/general.php b/templates/wg/general.php index 86eefe752..34147aef8 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -9,21 +9,40 @@

- +

- - + +
- - + +
+ +
+
+
+

+ .conf file to this device.") ?> +

+
+
+
+
+
+ + +
+
+
+
+
@@ -46,12 +65,10 @@
-
-
@@ -59,27 +76,9 @@
-
-
-
-

- .conf file to this device.") ?> -

-
-
-
-
-
- - -
-
-
-
- + - From 8c3531e6d2bc24136c26e7f7336a35508854c793 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 6 Jul 2021 23:13:32 +0100 Subject: [PATCH 221/300] Work in progress: SaveWireGuardUpload() --- includes/wireguard.php | 55 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/includes/wireguard.php b/includes/wireguard.php index c7f56cdd6..0ce233531 100644 --- a/includes/wireguard.php +++ b/includes/wireguard.php @@ -12,6 +12,8 @@ function DisplayWireGuardConfig() if (!RASPI_MONITOR_ENABLED) { if (isset($_POST['savewgsettings'])) { SaveWireGuardConfig($status); + } elseif (is_uploaded_file( $_FILES["wgFile"]["tmp_name"])) { + SaveWireGuardUpload($status, $_FILES['wgFile']); } elseif (isset($_POST['startwg'])) { $status->addMessage('Attempting to start WireGuard', 'info'); exec('sudo /bin/systemctl start wg-quick@wg0', $return); @@ -79,6 +81,59 @@ function DisplayWireGuardConfig() ); } +/** + * Validates uploaded .conf file, adds iptables post-up and + * post-down rules. + * + * @param object $status + * @param object $file + * @return object $status + */ +function SaveWireGuardUpload($status, $file) +{ + define('KB', 1024); + $tmp_destdir = '/tmp/'; + $auth_flag = 0; + + try { + // If undefined or multiple files, treat as invalid + if (!isset($file['error']) || is_array($file['error'])) { + throw new RuntimeException('Invalid parameters'); + } + + $upload = \RaspAP\Uploader\Upload::factory('wg',$tmp_destdir); + $upload->set_max_file_size(64*KB); + $upload->set_allowed_mime_types(array('text/plain')); + $upload->file($file); + + $validation = new validation; + $upload->callbacks($validation, array('check_name_length')); + $results = $upload->upload(); + + if (!empty($results['errors'])) { + throw new RuntimeException($results['errors'][0]); + } + + // Good file upload, do any post-processing + + // Set iptables rules + + // Move uploaded .conf from /tmp to destination + + + if ($return ==0) { + $status->addMessage('WireGuard configuration uploaded successfully', 'info'); + } else { + $status->addMessage('Unable to save WireGuard configuration', 'danger'); + } + return $status; + + } catch (RuntimeException $e) { + $status->addMessage($e->getMessage(), 'danger'); + return $status; + } +} + /** * Validate user input, save wireguard configuration * From c3b45a7915ab7db3643c166397e26fc496eb665b Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 7 Jul 2021 08:12:30 +0100 Subject: [PATCH 222/300] Update labels, add iptables rules toggle --- templates/wg/general.php | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/templates/wg/general.php b/templates/wg/general.php index 34147aef8..63cd9bf5a 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -15,7 +15,7 @@
- +
@@ -29,10 +29,23 @@

- .conf file to this device.") ?> + .conf file on this device.") ?>

+ +
+
+ + /> + + "> +

+ iptables Postup and PostDown rules for the configured AP interface (%s)."), $_SESSION['ap_interface']) ?> +

+
+
+
@@ -40,6 +53,8 @@
+
+
From d7baf5492f32b91b7af8e52079f4714d4b6feffa Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 7 Jul 2021 22:58:50 +0100 Subject: [PATCH 223/300] Set toggle state from template var --- templates/wg/general.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/wg/general.php b/templates/wg/general.php index 63cd9bf5a..00d840ac3 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -36,7 +36,7 @@
- + /> "> From fbe1348e15f7f3f8c3cb771e3303eb9d55a6aec9 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 7 Jul 2021 22:59:51 +0100 Subject: [PATCH 224/300] Added sudoers mv /tmp/wg/* --- installers/raspap.sudoers | 1 + 1 file changed, 1 insertion(+) diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index 2892c2275..1d51eb49d 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -48,6 +48,7 @@ www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/dnsmasqdata /etc/dnsmasq.d/090_adblock. 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:/bin/mv /tmp/wg/* /etc/wireguard/*.conf 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 * From 225bff59b60d9c70a00010b9b16e8162f9545af2 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 7 Jul 2021 23:01:47 +0100 Subject: [PATCH 225/300] Upload wg config, set postup/down rules, move to destination --- includes/wireguard.php | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/includes/wireguard.php b/includes/wireguard.php index 0ce233531..af81a6187 100644 --- a/includes/wireguard.php +++ b/includes/wireguard.php @@ -10,10 +10,11 @@ function DisplayWireGuardConfig() { $status = new StatusMessages(); if (!RASPI_MONITOR_ENABLED) { - if (isset($_POST['savewgsettings'])) { + $optRules = $_POST['wgRules']; + if (isset($_POST['savewgsettings']) && !is_uploaded_file($_FILES["wgFile"]["tmp_name"])) { SaveWireGuardConfig($status); - } elseif (is_uploaded_file( $_FILES["wgFile"]["tmp_name"])) { - SaveWireGuardUpload($status, $_FILES['wgFile']); + } elseif (isset($_POST['savewgsettings']) && is_uploaded_file($_FILES["wgFile"]["tmp_name"])) { + SaveWireGuardUpload($status, $_FILES['wgFile'], $optRules); } elseif (isset($_POST['startwg'])) { $status->addMessage('Attempting to start WireGuard', 'info'); exec('sudo /bin/systemctl start wg-quick@wg0', $return); @@ -63,6 +64,7 @@ function DisplayWireGuardConfig() "status", "wg_state", "serviceStatus", + "optRules", "wg_log", "peer_id", "wg_srvpubkey", @@ -87,9 +89,10 @@ function DisplayWireGuardConfig() * * @param object $status * @param object $file + * @param boolean $optRules * @return object $status */ -function SaveWireGuardUpload($status, $file) +function SaveWireGuardUpload($status, $file, $optRules) { define('KB', 1024); $tmp_destdir = '/tmp/'; @@ -114,12 +117,23 @@ function SaveWireGuardUpload($status, $file) throw new RuntimeException($results['errors'][0]); } - // Good file upload, do any post-processing + // Valid upload, get file contents + $tmp_wgconfig = $results['full_path']; + $tmp_contents = file_get_contents($tmp_wgconfig); // Set iptables rules + if (isset($optRules) && !preg_match('/PostUp|PostDown/m',$tmp_contents)) { + $rules[] = 'PostUp = '.getDefaultNetValue('wireguard','server','PostUp'); + $rules[] = 'PostDown = '.getDefaultNetValue('wireguard','server','PostDown'); + $rules[] = ''; + $rules = join(PHP_EOL, $rules); + $rules = preg_replace('/wlan0/m', $_SESSION['ap_interface'], $rules); + $tmp_contents = preg_replace('/^\s*$/ms', $rules, $tmp_contents, 1); + file_put_contents($tmp_wgconfig, $tmp_contents); + } - // Move uploaded .conf from /tmp to destination - + // Move uploaded file from to destination + system("sudo mv $tmp_wgconfig ". RASPI_WIREGUARD_CONFIG, $return); if ($return ==0) { $status->addMessage('WireGuard configuration uploaded successfully', 'info'); From 84fcedc203fdf7face427a751820ad204344ba1a Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 7 Jul 2021 23:24:49 +0100 Subject: [PATCH 226/300] Added get_public_ip() --- includes/functions.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/includes/functions.php b/includes/functions.php index b30ff8257..8ccd24302 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -778,3 +778,13 @@ public function check_name_length($object) } } +/* Resolves public IP address + * + * @return string $public_ip + */ +function get_public_ip() +{ + exec('wget https://ipinfo.io/ip -qO -', $public_ip); + return $public_ip[0]; +} + From 8374d032b392ba37ec5fa5f07007f25d7c39f137 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 7 Jul 2021 23:25:23 +0100 Subject: [PATCH 227/300] Update w/ common public_ip function --- includes/openvpn.php | 4 +--- includes/wireguard.php | 4 +++- templates/wg/general.php | 6 ++++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/includes/openvpn.php b/includes/openvpn.php index eae9c42d8..a89c3e180 100755 --- a/includes/openvpn.php +++ b/includes/openvpn.php @@ -42,11 +42,9 @@ function DisplayOpenVPNConfig() } exec('pidof openvpn | wc -l', $openvpnstatus); - exec('wget https://ipinfo.io/ip -qO -', $return); - $serviceStatus = $openvpnstatus[0] == 0 ? "down" : "up"; $auth = file(RASPI_OPENVPN_CLIENT_LOGIN, FILE_IGNORE_NEW_LINES); - $public_ip = $return[0]; + $public_ip = get_public_ip(); // parse client auth credentials if (!empty($auth)) { diff --git a/includes/wireguard.php b/includes/wireguard.php index af81a6187..162e3eef1 100644 --- a/includes/wireguard.php +++ b/includes/wireguard.php @@ -58,12 +58,14 @@ function DisplayWireGuardConfig() exec('pidof wg-crypt-wg0 | wc -l', $wgstatus); $serviceStatus = $wgstatus[0] == 0 ? "down" : "up"; $wg_state = ($wgstatus[0] > 0); + $public_ip = get_public_ip(); echo renderTemplate( "wireguard", compact( "status", "wg_state", "serviceStatus", + "public_ip", "optRules", "wg_log", "peer_id", @@ -132,7 +134,7 @@ function SaveWireGuardUpload($status, $file, $optRules) file_put_contents($tmp_wgconfig, $tmp_contents); } - // Move uploaded file from to destination + // Move processed file from tmp to destination system("sudo mv $tmp_wgconfig ". RASPI_WIREGUARD_CONFIG, $return); if ($return ==0) { diff --git a/templates/wg/general.php b/templates/wg/general.php index 00d840ac3..c9b63071d 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -3,6 +3,12 @@

+
+
+
+
+
+
aria-describedby="server-description"> From 2ccce60189494c081095f17599db0a493703b80e Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 8 Jul 2021 11:22:17 +0100 Subject: [PATCH 228/300] Simplify template, update save actions --- includes/wireguard.php | 12 +++++++----- templates/wg/general.php | 31 +++++++++++++++++-------------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/includes/wireguard.php b/includes/wireguard.php index 162e3eef1..cb66341b2 100644 --- a/includes/wireguard.php +++ b/includes/wireguard.php @@ -10,10 +10,12 @@ function DisplayWireGuardConfig() { $status = new StatusMessages(); if (!RASPI_MONITOR_ENABLED) { - $optRules = $_POST['wgRules']; - if (isset($_POST['savewgsettings']) && !is_uploaded_file($_FILES["wgFile"]["tmp_name"])) { + $optRules = $_POST['wgRules']; + $optConf = $_POST['wgCnfOpt']; + $optSrvEnable = $_POST['wgSrvEnable']; + if (isset($_POST['savewgsettings']) && $optConf == 'manual' && $optSrvEnable == 1 ) { SaveWireGuardConfig($status); - } elseif (isset($_POST['savewgsettings']) && is_uploaded_file($_FILES["wgFile"]["tmp_name"])) { + } elseif (isset($_POST['savewgsettings']) && $optConf == 'upload' && is_uploaded_file($_FILES["wgFile"]["tmp_name"])) { SaveWireGuardUpload($status, $_FILES['wgFile'], $optRules); } elseif (isset($_POST['startwg'])) { $status->addMessage('Attempting to start WireGuard', 'info'); @@ -30,7 +32,7 @@ function DisplayWireGuardConfig() } } - // fetch wg config + // fetch server config exec('sudo cat '. RASPI_WIREGUARD_CONFIG, $return); $conf = ParseConfig($return); $wg_srvpubkey = exec('sudo cat '. RASPI_WIREGUARD_PATH .'wg-server-public.key', $return); @@ -42,7 +44,7 @@ function DisplayWireGuardConfig() $wg_senabled = true; } - // todo: iterate multiple peer configs + // fetch client config exec('sudo cat '. RASPI_WIREGUARD_PATH.'client.conf', $preturn); $conf = ParseConfig($preturn); $wg_pipaddress = ($conf['Address'] == '') ? getDefaultNetValue('wireguard','peer','Address') : $conf['Address']; diff --git a/templates/wg/general.php b/templates/wg/general.php index c9b63071d..15d2f9e32 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -9,22 +9,13 @@
-
-
- aria-describedby="server-description"> - -
-

- -

-
- +
- +
@@ -67,10 +58,20 @@
-

- wg0.conf WireGuard configuration on this device.") ?> -

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

+ + + .conf file on this device.") ?> + +

+
+
@@ -94,6 +95,8 @@
+
+
From ab3e147bbfe709538ed8ea466dafcf269bd6f3c2 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 13 Jul 2021 12:51:09 +0100 Subject: [PATCH 229/300] Update wg messages for en_US locale --- locale/en_US/LC_MESSAGES/messages.mo | Bin 26387 -> 28313 bytes locale/en_US/LC_MESSAGES/messages.po | 38 ++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/locale/en_US/LC_MESSAGES/messages.mo b/locale/en_US/LC_MESSAGES/messages.mo index f51f7c62d77de2fcf26b546fbdae30f2fa79006b..b1d575103df844f9061bbe9f73cf985b650b4813 100644 GIT binary patch literal 28313 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 delta 7766 zcmcK9eSDAg9>?+T?rnzMFk{BXHgi8C_nS>@44eDS&D@VEB8HR6Z=&2JH!Ygn6cOsk z%{q#5dx}gcM?z6igov~_MLMtduJ0bl<8l5z*W-LVzu)h5UEk~Ty}7R6&N(a3yA>XH zbA1!!x!iCJbu*?G`d2jOB<1_n)M`xEaARVz8$N-Pu`*u32)u><7+Bkw>R1~qU@Kei zh&`xhVg_!&AS^{5e($&9d<(>9DyM?4)uTn48#RUM`jgP#Z9OL zKS14o49U@ak9v+%?GT!@-#zbGfsTBwd2pb~3q z+mo>|^>hrtd`!Vbs2M(qO6(SDsqRP7y$6NBXlDe~P>DpKE{H*musv#oT~K?Y7b<}< zI2Wg(X6&9l@6I%~pdN~9?}2)7U#x+{kW0-|F$_;@@+u9XxEUMbLDZC8N6m;weP<*g z$i_9P+(KL>8uTqRWYqliDg@#wyr@K-p*G(t)JV3Yrt%0@!LzpgDh5+Gan6XUqE<5;^_r!iW@el{KLyp_OjLh`=*RQT zMhY6ycJ#;7sEmKm1*qW!Ktcv3qaIj_>c}(RNwf;;i&+b`rmax}NkZK}7Bxc?QT@)b7NOp@ zt?|siMsS#hN_Yx21K*=&;1;qEjXy85I&6zdC>et=3zgX8s2RvdCFnvWv=Fr^3sKh< zp%U4Q8u;E8%)c%;N<%&#NBvl&wRAej#R%$8q8|7ncEgRRDZGh#;2qRJ+#Ye3Dgc#m zBzj>3)cIzp`w}r2ySOOiP#9=W?6)TlqcZ-=dd;@~fl9b?D`&F>qh_uHcEB{$$md}o zF16>^BcBwr-L`*)O2~Dcf-=2_8lgvPXG9^W1R}9NHpd8@he}{G>NVPmdf;cMr8|S_ z_!8>=QY=7EzE+y4XR$0XBypEnPeHHAcGL}foD-(RdIq&tmr+x88+E;RTc@J{)C|-` zJs=L1NPARbT~P`3w(Y~M6EH~c{|pMc@dea_im)MWK>cccX1$J@DPO+FTEjrpjD=z~ zY=kv1*|rZxJ@^S!!cW`!JnTk&1&+}B{~d*%*r~lSskjuo;2BK8+AM5e%)u$R8#RSZ z5*$0CW~MjlfjL+e^H4Lo05!0cSQ9s56dpjAI=VzbBf5*4i3g|-d=s7a+Ni0FMvb&7 zs-w>K{4k8CJ_ciOC054+s07ZSX6_1V@7zKq^jjkHugu*#I`4Zm)TgsKW?(;C-+=lK z9I=+7I*v?o*1kFF0UdD{rlKD70F{VGvSS#ky#;DX+9bP-VX&qX4eFr4Iv?GsFU3dP z_<&$6^`o7fzk+Y0rh3Gq&Ra1Fwdt0j9=H}YW4mm9KlY%08a1$}&d%P+b5YP#%)>A& zL=W7Cn(AUyheuHfUceB%ih6)Hik8q9)lV2|>FT2r?0~wzJBH&Z)N^KAU27<)!`)a5 zKS!L}ithF;W1+Vi=nC7F&&bRO#MSYq4PV>&$Ztx(sep!Qfc4%Yi$ zKtUrpXT6BZ{0eI1_mInsA1|$@dLb&&)u<6~K_A?0+Ye%6>c>$(+xL(`8~-$ChWnxt zn}k)>@k|O@^JVCV%TWovhFZ%v&==oFKl})_H$F!ta2e;~9n?(bcX!S|gDt2Rq1r#e zig*(Bq52kG(G-5CPyy?(MME(b8)7nQ%EqH+WDaU1E3h`6N8NYd*1hkOKK;J&hzedoqk29hKRE9%PndhQrpaARO%czmR zi+=beYHBZ_mgow4;%(G@cWry+zD_+9gJ^GtdTu8dg$N4WQJIfNb(n`*<9yVEmZ3kc zwr;@~>bp>howxpJ4e#gt6`X)NKM^(ZC8zHA6R1{d%x-WhjR7eA9%2Mv#JfE&8K6 z9)+5LN!S<{p*q}+O6VXe;WMbjenQQ_Jye2!p%U^N=xoYh)ODe#1Y)u5@BajQK{w3j zL^|rnq6F2!6^y`NP!9|mOQ%S$m@6U}wrwP#F)hK4#lzpkA}ZsLfW0nz;{=gStNt3ve!~ zU$3DyF&Blh%uugM9O{O4wmr?7h1#`aP%}0eb^UXww_z!&pY^B*Y)2*XJ}R+~Q3;)} z?cZ5lHz;Vtf1qxxG|YKWC^n=Xg|#rlIvzDs3s7sg40V4IR>QZj1|GESU!xxUGb-Uf zZQW;h*&cG45DFtWF&um1Axy;p8oOW?rr=uaiO0CASPiU6JsS1DKba&7n!+5^h^C`vViu}{1-5-HYAQFPM*23YqYv%*Z!wNro3m+5co zd^*fuL^x5;wo&|_;{{sYv#njU{#s)l`lNQ@#-C9e@ix(bsQFN5?WkWPbcE8beNujS z*z5G)7v83^E?&pg#3XysMyy2p2ev*z>;JMU_U0w_M4YWBQh&;}`Ox+i<;}!a+qMOF z5Pv5c+k0Yh74@aW??eoBZQSzXHCt#$yX&R$R@AO;LPQe*oUDmDv>CP0bd)-n-Pn?A z^sYZ*+sdg;`x+{L5NmDYLVSw|=iET7L)_EvzmCB)b|#{Uoz$N$Yvon3R>IbtJA|Lv zwsLw?Na380+C+WApL(7eI66C+TK1YA)H7`PMLoYB4PG=fL%kOKYMMnvH+vC9Q=jrd zJgW-FTw)W^htLq#6MYFC|0Jq&{Z;&tc$X+YPTE3$^dQ#h=2rGWP4QcM!8EH_Kz#X7 zT}6H2e7XLHZO_L?iGH;47MJ}hR->$+VI2j;Z^Zk=aKf8&e?wOj3eAZ*RK6f|Bv5Zg zBvbyHh@z}xG4Urckos(FOthf<4OS<5P~L?rh+?ArI7eXy@ekV&WBs=#ucfV&bHWz( zVGtdT#3863p%fyS@=(k|eYWotI(~I1`_(I_PAweq zboeQbBSsNX#4;kAh~%7(`3|N5^;?wxW$Q!mCc&S0W&hhwBzh5d2_1zFW;^!e-iAa+ zqK}Jzbi@6h_=S2fPPXmBlln8Z{)$!q7vB?< z-yuFH-XvUYX-p()5biWoz~k7D(D4Itm~f-LH_?W2FAOAfJV~ULmH0CbJJ8-9PZNVE ze}b>8ozPK{XhEDJCTjh=bI|}IlhAR57)`V#B8a}Wouc`Wh@hU1=?}Hvq27@wB6Ku& zFqf>);|5|c;bYsr!|r-{Kc?VI1QQ1c9W#lpL~EiA59&&+q)?g5 zmjC{3J~hq@Pk*dy1ia9{NEg qFlyrHoW$v4;|e#8Ni5u)+qHP@xSnpsfsfDkC=N;4=J9`j%6|ZNfmi1M diff --git a/locale/en_US/LC_MESSAGES/messages.po b/locale/en_US/LC_MESSAGES/messages.po index b1defed5d..c1b9ff474 100644 --- a/locale/en_US/LC_MESSAGES/messages.po +++ b/locale/en_US/LC_MESSAGES/messages.po @@ -1026,14 +1026,44 @@ msgstr "Invalid custom host found on line " msgid "Tunnel settings" msgstr "Tunnel settings" +msgid "Configuration Method" +msgstr "Configuration Method" + +msgid "Upload file" +msgstr "Upload file" + +msgid "Create manually" +msgstr "Create manually" + +msgid "Upload a WireGuard config" +msgstr "Upload a WireGuard config" + +msgid "This option uploads and installs an existing WireGuard .conf file on this device." +msgstr "This option uploads and installs an existing WireGuard .conf file on this device." + +msgid "Apply iptables rules for AP interface" +msgstr "Apply iptables rules for AP interface" + +msgid "Recommended if you wish to forward network traffic from the wg0 interface to clients connected on the AP interface." +msgstr "Recommended if you wish to forward network traffic from the wg0 interface to clients connected on the AP interface." + +msgid "This option adds iptables Postup and PostDown rules for the configured AP interface (%s)." +msgstr "This option adds iptables Postup and PostDown rules for the configured AP interface (%s)." + +msgid "Select WireGuard configuration file (.conf)" +msgstr "Select WireGuard configuration file (.conf)" + +msgid "Create a local WireGuard config" +msgstr "Create a local WireGuard config" + 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 "Enable this option to secure network traffic by creating an encrypted tunnel between RaspAP and configured peers." +msgstr "Enable this option to secure network traffic by creating an encrypted 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 "This setting generates a new WireGuard .conf file on this device." +msgstr "This setting generates a new WireGuard .conf file on this device." msgid "Local public key" msgstr "Local public key" From 1b7169edd4fef003f11a094a4180bfbf326b9890 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 14 Jul 2021 07:18:07 +0100 Subject: [PATCH 230/300] Tweak wg template --- templates/wg/general.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/wg/general.php b/templates/wg/general.php index 15d2f9e32..25d74d124 100644 --- a/templates/wg/general.php +++ b/templates/wg/general.php @@ -1,7 +1,7 @@
-
+

@@ -31,7 +31,7 @@
-
+
/> From 98533c3ee84c19000e2fbfd9c09a064974f98f48 Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 15 Jul 2021 08:49:35 +0100 Subject: [PATCH 231/300] Update release version --- README.md | 2 +- includes/defaults.php | 2 +- index.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b371b9b18..0102b431e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![](https://i.imgur.com/FRU1tXF.png) -[![Release 2.7.4](https://img.shields.io/badge/release-2.7.4-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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/) +[![Release 2.7.5](https://img.shields.io/badge/release-2.7.5-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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. diff --git a/includes/defaults.php b/includes/defaults.php index 280d1c3ef..026f374a4 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -6,7 +6,7 @@ $defaults = [ 'RASPI_BRAND_TEXT' => 'RaspAP', - 'RASPI_VERSION' => '2.7.4', + 'RASPI_VERSION' => '2.7.5', 'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json', 'RASPI_ADMIN_DETAILS' => RASPI_CONFIG.'/raspap.auth', 'RASPI_WIFI_AP_INTERFACE' => 'wlan0', diff --git a/index.php b/index.php index 0f9bb7a66..3e6e9ff6d 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.4 + * @version 2.7.5 * @link https://github.com/raspap/raspap-insiders/ * @link https://raspap.com/ * @see http://sirlagz.net/2013/02/08/raspap-webgui/ From 59abc641d6f5c2af46515bc917fe0474b7b3727f Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Fri, 16 Jul 2021 21:40:28 +0200 Subject: [PATCH 232/300] Implement firewall - settings in iptables_rules.json - creates a script under /tmp/iptables_raspap.sh and executes it - no installer yet - to do: deal with Bridge and VPN settings --- config/iptables_rules.json | 168 +++++++++++++++++++++++++++++++++++ includes/firewall.php | 174 +++++++++++++++++++++++++++++++++++++ installers/raspap.sudoers | 1 + templates/firewall.php | 70 +++++++++++++++ 4 files changed, 413 insertions(+) create mode 100644 config/iptables_rules.json create mode 100644 includes/firewall.php create mode 100644 templates/firewall.php diff --git a/config/iptables_rules.json b/config/iptables_rules.json new file mode 100644 index 000000000..fa23d7071 --- /dev/null +++ b/config/iptables_rules.json @@ -0,0 +1,168 @@ +{ + "info": "IPTABLES rules. $...$ expressions will be replaces automatically ($INTERFACE$, $PORT$, $IPADDRESS$)", + "rules_v4_file": "/etc/iptables/rules.v4", + "rules_v6_file": "/etc/iptables/rules.v6", + "order": [ "pre_rules", "restriction_rules", "main_rules", "exception_rules" ], + "pre_rules": [ + { + "name": "firewall policies", + "fw-state": true, + "comment": "Policy rules (firewall)", + "rules": [ + "-P INPUT DROP", + "-P FORWARD ACCEPT", + "-P OUTPUT ACCEPT", + "-t nat -P PREROUTING ACCEPT", + "-t nat -P POSTROUTING ACCEPT", + "-t nat -P INPUT ACCEPT", + "-t nat -P OUTPUT ACCEPT" + ] + }, + { + "name": "policies", + "fw-state": false, + "comment": "Policy rules", + "rules": [ + "-P INPUT ACCEPT", + "-P FORWARD ACCEPT", + "-P OUTPUT ACCEPT", + "-t nat -P PREROUTING ACCEPT", + "-t nat -P POSTROUTING ACCEPT", + "-t nat -P INPUT ACCEPT", + "-t nat -P OUTPUT ACCEPT" + ] + }, + { + "name": "loopback", + "fw-state": true, + "comment": "allow loopback device", + "rules": [ + "-A INPUT -i lo -j ACCEPT", + "-A OUTPUT -o lo -j ACCEPT" + ] + }, + { + "name": "ping", + "fw-state": true, + "comment": "allow ping request and echo", + "rules": [ + "-A INPUT -p icmp --icmp-type 8/0 -j ACCEPT", + "-A INPUT -p icmp --icmp-type 0/0 -j ACCEPT" + ] + }, + { + "name": "ntp", + "fw-state": true, + "comment": "allow ntp request via udp (tcp should work w/o rule)", + "rules": [ + "-A INPUT -p udp --sport 123 -j ACCEPT" + ] + }, + { + "name": "dns", + "fw-state": true, + "comment": "allow dns request via tcp and udp", + "rules": [ + "-A INPUT -p udp -m multiport --sport 53,853 -j ACCEPT", + "-A INPUT -p tcp -m multiport --sport 53,853 -j ACCEPT" + ] + } + ], + "main_rules": [ + { + "name": "accesspoint", + "fw-state": true, + "comment": "Access point interface by default no restrictions", + "dependson": [ + { "var": "ap-device", "type": "string", "replace": "$INTERFACE$" } + ], + "rules": [ + "-A INPUT -i $INTERFACE$ -j ACCEPT", + "-A OUTPUT -o $INTERFACE$ -j ACCEPT" + ] + }, + { + "name": "clients", + "fw-state": true, + "comment": "Rules for client interfaces (includes tun device)", + "rules": [ + "-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT" + ] + }, + { + "name": "openvpn", + "comment": "Rules for tunnel device (tun)", + "dependson": [ + { "var": "openvpn-enable", "type": "bool" }, + { "var": "openvpn-serverip", "type": "string", "replace": "$IPADDRESS$" }, + { "var": "client-device", "type": "string", "replace": "$INTERFACE$" } + ], + "rules": [ + "-A FORWARD -i tun+ -o $INTERFACE$ -m state --state RELATED,ESTABLISHED -j ACCEPT", + "-A FORWARD -i $INTERFACE$ -o tun+ -j ACCEPT", + "-t nat -A POSTROUTING -o tun+ -j MASQUERADE" + ] + } + ], + "exception_rules": [ + { + "name": "ssh", + "fw-state": true, + "comment": "Allow ssh access to RaspAP on port 22", + "dependson": [ + { "var": "ssh-enable", "type": "bool" } + ], + "rules": [ + "-A INPUT -p tcp --dport 22 -j ACCEPT" + ] + }, + { + "name": "http", + "fw-state": true, + "comment": "Allow access to RaspAP GUI (https)", + "dependson": [ + { "var": "http-enable", "type": "bool" } + ], + "rules": [ + "-A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT" + ] + }, + { + "name": "interface", + "fw-state": true, + "comment": "Exclude interface from firewall", + "dependson": [ + { "var": "excl-devices", "type": "list", "replace": "$INTERFACE$" } + ], + "rules": [ + "-A INPUT -i $INTERFACE$ -j ACCEPT", + "-A OUTPUT -o $INTERFACE$ -j ACCEPT" + ] + }, + { + "name": "ipaddress", + "fw-state": true, + "comment": "allow access from/to IP", + "dependson": [ + { "var": "excluded-ips", "type": "list", "replace": "$IPADDRESS$" } + ], + "rules": [ + "-A INPUT -s $IPADDRESS$ -j ACCEPT", + "-A INPUT -d $IPADDRESS$ -j ACCEPT" + ] + } + ], + "restriction_rules": [ + { + "name": "ipaddress", + "fw-state": true, + "dependson": [ + { "var": "restricted-ips", "type": "list", "replace": "$IPADDRESS$" } + ], + "comment": "Block access from IP-address", + "rules": [ + "-A INPUT -s $IPADDRESS$ -j DROP" + ] + } + ] +} diff --git a/includes/firewall.php b/includes/firewall.php new file mode 100644 index 000000000..ceac3748f --- /dev/null +++ b/includes/firewall.php @@ -0,0 +1,174 @@ +"; +// print_r($ipt); + $txt = "#!/bin/bash\n"; + $txt .= "iptables -F\n"; + $txt .= "iptables -X\n"; + $txt .= "iptables -t nat -F\n"; + file_put_contents(RASPAP_IPTABLES_SCRIPT, $txt); + if ( empty($conf) || empty($ipt) ) return false; + $count=0; + foreach ( $ipt["order"] as $idx ) { + if ( isset($ipt[$idx]) ) { +// echo "Handle $idx \n"; + foreach ( $ipt[$idx] as $i => $sect ) { + if ( isRuleEnabled($sect, $conf) ) { +// echo " rule $i name ".$sect["name"]."\n"; + $str_rules= createRuleStr($sect, $conf); + if ( !empty($str_rules) ) { + file_put_contents(RASPAP_IPTABLES_SCRIPT, $str_rules, FILE_APPEND); + ++$count; + } + } + } + } + } +// echo "Firewall ON"; +//echo ""; + if ( $count > 0 ) { + exec("chmod +x ".RASPAP_IPTABLES_SCRIPT); + exec("sudo ".RASPAP_IPTABLES_SCRIPT); +// exec("sudo iptables-save > /etc/iptables/rules.v4"); +// unlink(RASPAP_IPTABLES_SCRIPT); + } + return ($count > 0); +} + +function WriteFirewallConf($conf) { + if ( is_array($conf) ) write_php_ini($conf,RASPAP_FIREWALL_CONF); +} + + +function ReadFirewallConf() { + if ( file_exists(RASPAP_FIREWALL_CONF) ) { + $conf = parse_ini_file(RASPAP_FIREWALL_CONF); + } else { + $conf = array(); + $conf["firewall-enable"] = false; + $conf["openvpn-enable"] = false; + $conf["openvpn-serverip"] = ""; + $conf["wireguard-enable"] = false; + $conf["wireguard-serverip"] = ""; + $conf["ssh-enable"] = false; + $conf["http-enable"] = false; + $conf["excl-devices"] = ""; + $conf["excluded-ips"] = ""; + $conf["ap-device"] = ""; + $conf["client-device"] = ""; + $conf["restricted-ips"] = ""; + } + return $conf; +} + +function DisplayFirewallConfig() +{ + + $status = new StatusMessages(); + + $json = file_get_contents(RASPAP_IPTABLES_CONF); + $ipt_rules = json_decode($json, true); + + getWifiInterface(); + $ap_device = $_SESSION['ap_interface']; + $clients = getClients(); + $fw_conf = ReadFirewallConf(); + $fw_conf["ap-device"] = $ap_device; + $id=findCurrentClientIndex($clients); + if ( $id >= 0 ) $fw_conf["client-device"] = $clients["device"][$id]["name"]; + if (!empty($_POST)) { + $fw_conf["ssh-enable"] = isset($_POST['ssh-enable']); + $fw_conf["http-enable"] = isset($_POST['http-enable']); + $fw_conf["firewall-enable"] = isset($_POST['firewall-enable']) || isset($_POST['apply-firewall']); + if ( isset($_POST['firewall-enable']) ) $status->addMessage(_('Firewall is now enabled'), 'success'); + if ( isset($_POST['apply-firewall']) ) $status->addMessage(_('Firewall settings changed'), 'success'); + if ( isset($_POST['firewall-disable']) ) $status->addMessage(_('Firewall is now disabled'), 'warning'); + if ( isset($_POST['save-firewall']) ) $status->addMessage(_('Firewall settings saved. Firewall is still disabled.'), 'success'); + WriteFirewallConf($fw_conf); + setFirewall(); + } + echo renderTemplate("firewall", compact( + "status", + "ap_device", + "clients", + "fw_conf", + "ipt_rules") + ); +} diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index 7ce40f28d..ae7379106 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -62,3 +62,4 @@ 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 +www-data ALL=(ALL) NOPASSWD:/tmp/iptables_raspap.sh diff --git a/templates/firewall.php b/templates/firewall.php new file mode 100644 index 000000000..39e41b711 --- /dev/null +++ b/templates/firewall.php @@ -0,0 +1,70 @@ +
+
+
+
+
+
+ +
+
+
+
+ showMessages(); ?> +

+ + + + + +
+
+

+
+
+ +
+ +
+
+
+
+ > + +
+
+ > + +
+

+ +

+
+
+ + " name="apply-firewall" /> + " name="firewall-disable" data-toggle="modal" data-target="#firewallModal"/> + + " name="save-firewall" /> + " name="firewall-enable" data-toggle="modal" data-target="#firewallModal"/> + +
+
+ +
+
+
+ + +
+
+
+
+ + " aria-describedby="exclusion-description" > +

+ Current client devices: $str_clients
The access point ". $ap_device ." is per default excluded.") ?>
+

+
+
" name="apply-firewall" /> " name="firewall-disable" data-toggle="modal" data-target="#firewallModal"/> From 393292f8722f95de11d752e6939d25d6d1dd20e2 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Wed, 21 Jul 2021 16:02:21 +0200 Subject: [PATCH 240/300] Add VPN server IPs to Firewall GUI --- includes/firewall.php | 74 +++++++++++++++++++++++++++--------------- templates/firewall.php | 25 ++++++++++---- 2 files changed, 66 insertions(+), 33 deletions(-) diff --git a/includes/firewall.php b/includes/firewall.php index dc2397cf8..6839a2647 100644 --- a/includes/firewall.php +++ b/includes/firewall.php @@ -39,7 +39,7 @@ function createRuleStr(&$sect, &$conf) { $repl=$val=""; switch ( $dep["type"] ) { case "list": - if ( isset($dep["var"]) && !empty($conf[$dep["var"]]) ) $val = explode(',', $conf[$dep["var"]]); + if ( isset($dep["var"]) && !empty($conf[$dep["var"]]) ) $val = explode(' ', $conf[$dep["var"]]); if ( !empty($val) && isset($dep["replace"]) ) $repl=$dep["replace"]; break; case "string": @@ -103,9 +103,9 @@ function configureFirewall() { } function WriteFirewallConf($conf) { - $ret = false; - if ( is_array($conf) ) write_php_ini($conf,RASPAP_FIREWALL_CONF); - return $ret; + $ret = false; + if ( is_array($conf) ) write_php_ini($conf,RASPAP_FIREWALL_CONF); + return $ret; } @@ -115,10 +115,6 @@ function ReadFirewallConf() { } else { $conf = array(); $conf["firewall-enable"] = false; - $conf["openvpn-enable"] = false; - $conf["openvpn-serverip"] = ""; - $conf["wireguard-enable"] = false; - $conf["wireguard-serverip"] = ""; $conf["ssh-enable"] = false; $conf["http-enable"] = false; $conf["excl-devices"] = ""; @@ -127,26 +123,32 @@ function ReadFirewallConf() { $conf["client-device"] = ""; $conf["restricted-ips"] = ""; } + return $conf; +} -# get openvpn server IP (if existing) - if ( RASPI_OPENVPN_ENABLED && file_exists(RASPI_OPENVPN_CLIENT_CONFIG) ) { - exec('cat '.RASPI_OPENVPN_CLIENT_CONFIG.' | sed -rn "s/^remote\s*([a-z0-9\.\-\_]*)\s*([0-9]*).*$/\1/ip" ', $ret); - if ( !empty($ret) ) { - $ip = $ret[0]; - $ip = ( filter_var($ip, FILTER_VALIDATE_IP) !== false ) ? $ip : gethostbyname($ip); - if ( !empty($ip) ) { - $conf["openvpn-serverip"] = "$ip"; - $conf["openvpn-enable"] = true; - } +function getVPN_IPs() { + $ips = ""; + # get openvpn server IPs for UDP (if existing) + if ( RASPI_OPENVPN_ENABLED && ($fconf = glob(RASPI_OPENVPN_CLIENT_PATH ."/*.conf")) !== false && !empty($fconf) ) { + foreach ( $fconf as $f ) { + exec('cat '.$f.' | sed -rn "s/^remote\s*([a-z0-9\.\-\_]*)\s*([0-9]*).*$/\1/ip" ', $result); + $ip = (isset($result[0])) ? $result[0] : ""; + unset($result); + exec('cat '.$f.' | sed -rn "s/^proto\s*([a-z]*).*$/\1/ip" ', $result); + $proto = (isset($result[0])) ? $result[0] : ""; + if ( !empty($ip) && trim(strtolower($proto)) === "udp" ) { + $ip = gethostbyname($ip); + if ( filter_var($ip,FILTER_VALIDATE_IP) && strpos($ips, $ip) === false ) $ips .= " $ip"; + } } } -# get wireguard server IP (if existing) - if ( RASPI_WIREGUARD_ENABLED && file_exists(RASPI_WIREGUARD_CONFIG) ) { -# search for endpoint + # get wireguard server IPs for UDP (if existing) + if ( RASPI_WIREGUARD_ENABLED && ($fconf = glob(RASPI_WIREGUARD_PATH ."/*.conf")) !== false && !empty($fconf) ) { } - return $conf; + return trim($ips); } + function DisplayFirewallConfig() { @@ -154,7 +156,6 @@ function DisplayFirewallConfig() $json = file_get_contents(RASPAP_IPTABLES_CONF); $ipt_rules = json_decode($json, true); - getWifiInterface(); $ap_device = $_SESSION['ap_interface']; $clients = getClients(); @@ -179,20 +180,41 @@ function DisplayFirewallConfig() if ( isset($_POST['save-firewall']) ) $status->addMessage(_('Firewall settings saved. Firewall is still disabled.'), 'success'); if ( isset($_POST['excl-devices']) ) { $excl = filter_var($_POST['excl-devices'], FILTER_SANITIZE_STRING); - $excl = str_replace(' ', '', $excl); - if ( !empty($excl) && $fw_conf["excl-devices"] != $excl ) { + $excl = str_replace(',', ' ', $excl); + $excl = trim(preg_replace('/\s+/', ' ', $excl)); + if ( $fw_conf["excl-devices"] != $excl ) { $status->addMessage(_('Exclude devices '. $excl), 'success'); $fw_conf["excl-devices"] = $excl; } } + if ( isset($_POST['excluded-ips']) ) { + $excl = filter_var($_POST['excluded-ips'], FILTER_SANITIZE_STRING); + $excl = str_replace(',', ' ', $excl); + $excl = trim(preg_replace('/\s+/', ' ', $excl)); + if ( !empty($excl) ) { + $excl = explode(' ',$excl); + $str_excl = ""; + foreach ( $excl as $ip ) { + if ( filter_var($ip,FILTER_VALIDATE_IP) ) $str_excl .= "$ip "; + else $status->addMessage(_('Exclude IP address '. $ip . ' failed - not a valid IP address'), 'warning'); + } + } + $str_excl = trim($str_excl); + if ( $fw_conf["excluded-ips"] != $str_excl ) { + $status->addMessage(_('Exclude IP address(es) '. $str_excl ), 'success'); + $fw_conf["excluded-ips"] = $str_excl; + } + } WriteFirewallConf($fw_conf); configureFirewall(); } + $vpn_ips = getVPN_IPs(); echo renderTemplate("firewall", compact( "status", "ap_device", "str_clients", "fw_conf", - "ipt_rules") + "ipt_rules", + "vpn_ips") ); } diff --git a/templates/firewall.php b/templates/firewall.php index e20217e46..e2b1ec6bd 100644 --- a/templates/firewall.php +++ b/templates/firewall.php @@ -18,34 +18,45 @@
-

+

No incoming UDP traffic is allowed.
There are no restrictions for the access point $ap_device.") ?>

-
+
- > + >
> - +

- +

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

- Current client devices: $str_clients
The access point ". $ap_device ." is per default excluded.") ?>
+ Current client devices: $str_clients
The access point ". $ap_device ." is per default excluded.") ?>
+

+
+
+
+
+
+ + " aria-describedby="excl-ips-description" > +

+ This is required for an OpenVPN via UDP or Wireguard connection.") ?> + The list of configured VPN server IP addresses: ". $vpn_ips. "") ?>

From 0a6e48a953bba13163cad1e1477aeb85cb52bc0d Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Wed, 21 Jul 2021 17:56:01 +0200 Subject: [PATCH 241/300] Fix display of VPN IPs --- includes/firewall.php | 1 + templates/firewall.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/includes/firewall.php b/includes/firewall.php index 6839a2647..90713d34e 100644 --- a/includes/firewall.php +++ b/includes/firewall.php @@ -131,6 +131,7 @@ function getVPN_IPs() { # get openvpn server IPs for UDP (if existing) if ( RASPI_OPENVPN_ENABLED && ($fconf = glob(RASPI_OPENVPN_CLIENT_PATH ."/*.conf")) !== false && !empty($fconf) ) { foreach ( $fconf as $f ) { + unset($result); exec('cat '.$f.' | sed -rn "s/^remote\s*([a-z0-9\.\-\_]*)\s*([0-9]*).*$/\1/ip" ', $result); $ip = (isset($result[0])) ? $result[0] : ""; unset($result); diff --git a/templates/firewall.php b/templates/firewall.php index e2b1ec6bd..478ed652f 100644 --- a/templates/firewall.php +++ b/templates/firewall.php @@ -56,7 +56,7 @@ " aria-describedby="excl-ips-description" >

This is required for an OpenVPN via UDP or Wireguard connection.") ?> - The list of configured VPN server IP addresses: ". $vpn_ips. "") ?> + The list of configured VPN server IP addresses: ". $vpn_ips. "") ?>

From 1855f40f9dc36c1326a01efe6f156aaee6ebf30f Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Sat, 24 Jul 2021 15:04:01 +0200 Subject: [PATCH 242/300] Add masquerade rule for NAT Add default NAT POSTROUTING rule to masquerade addresses --- config/iptables_rules.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/config/iptables_rules.json b/config/iptables_rules.json index 0e6618ecc..df2b126b1 100644 --- a/config/iptables_rules.json +++ b/config/iptables_rules.json @@ -81,6 +81,13 @@ "-A OUTPUT -o $INTERFACE$ -j ACCEPT" ] }, + { + "name": "NAT for access point", + "comment": "Masquerading needed for access point", + "rules": [ + "-t nat -A POSTROUTING -j MASQUERADE" + ] + }, { "name": "clients", "fw-state": true, From 2f1a6af0baad75be9bc880b58412569e3d7aafbd Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Sun, 25 Jul 2021 15:42:46 +0200 Subject: [PATCH 243/300] Add IPv6 to Firewall --- config/iptables_rules.json | 15 +++++++++++++++ includes/firewall.php | 29 ++++++++++++++++++++++++----- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/config/iptables_rules.json b/config/iptables_rules.json index df2b126b1..d9b6f5f9b 100644 --- a/config/iptables_rules.json +++ b/config/iptables_rules.json @@ -44,12 +44,23 @@ { "name": "ping", "fw-state": true, + "ip-version": 4, "comment": "allow ping request and echo", "rules": [ "-A INPUT -p icmp --icmp-type 8/0 -j ACCEPT", "-A INPUT -p icmp --icmp-type 0/0 -j ACCEPT" ] }, + { + "name": "ping IPv6", + "fw-state": true, + "ip-version": 6, + "comment": "allow ping request and echo for IPv6", + "rules": [ + "-A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT", + "-A INPUT -p icmpv6 --icmpv6-type echo-reply -j ACCEPT" + ] + }, { "name": "ntp", "fw-state": true, @@ -99,6 +110,7 @@ { "name": "openvpn", "comment": "Rules for tunnel device (tun)", + "ip-version": 4, "dependson": [ { "var": "openvpn-enable", "type": "bool" }, { "var": "openvpn-serverip", "type": "string", "replace": "$IPADDRESS$" }, @@ -114,6 +126,7 @@ { "name": "wireguard", "comment": "Rules for wireguard device (wg)", + "ip-version": 4, "dependson": [ { "var": "wireguard-enable", "type": "bool" }, { "var": "wireguard-serverip", "type": "string", "replace": "$IPADDRESS$" }, @@ -164,6 +177,7 @@ { "name": "ipaddress", "fw-state": true, + "ip-version": 4, "comment": "allow access from/to IP", "dependson": [ { "var": "excluded-ips", "type": "list", "replace": "$IPADDRESS$" } @@ -178,6 +192,7 @@ { "name": "ipaddress", "fw-state": true, + "ip-version": 4, "dependson": [ { "var": "restricted-ips", "type": "list", "replace": "$IPADDRESS$" } ], diff --git a/includes/firewall.php b/includes/firewall.php index 90713d34e..1b7636229 100644 --- a/includes/firewall.php +++ b/includes/firewall.php @@ -4,6 +4,7 @@ require_once 'includes/functions.php'; define('RASPAP_IPTABLES_SCRIPT',"/tmp/iptables_raspap.sh"); +define('RASPAP_IP6TABLES_SCRIPT',"/tmp/ip6tables_raspap.sh"); function getDependson(&$rule, &$conf) { if ( isset($rule["dependson"][0]) ) { @@ -64,20 +65,33 @@ function createRuleStr(&$sect, &$conf) { } $str=""; foreach ( $rs as $r ) { - if ( !preg_match('/\$[a-z0-9]*\$/i',$r) ) $str .= "iptables ".$r."\n"; + if ( !preg_match('/\$[a-z0-9]*\$/i',$r) ) $str .= '$IPT '.$r."\n"; } return $str; } +function isIPv4(&$rule) { + return !isset($rule["ip-version"]) || strstr($rule["ip-version"],"4") !== false; +} + +function isIPv6(&$rule) { + return !isset($rule["ip-version"]) || strstr($rule["ip-version"],"6") !== false; +} + function configureFirewall() { $json = file_get_contents(RASPAP_IPTABLES_CONF); $ipt = json_decode($json, true); $conf = ReadFirewallConf(); $txt = "#!/bin/bash\n"; - $txt .= "iptables -F\n"; - $txt .= "iptables -X\n"; - $txt .= "iptables -t nat -F\n"; file_put_contents(RASPAP_IPTABLES_SCRIPT, $txt); + file_put_contents(RASPAP_IP6TABLES_SCRIPT, $txt); + file_put_contents(RASPAP_IPTABLES_SCRIPT, 'IPT="iptables"'."\n", FILE_APPEND); + file_put_contents(RASPAP_IP6TABLES_SCRIPT, 'IPT="ip6tables"'."\n", FILE_APPEND); + $txt = "\$IPT -F\n"; + $txt .= "\$IPT -X\n"; + $txt .= "\$IPT -t nat -F\n"; + file_put_contents(RASPAP_IPTABLES_SCRIPT, $txt, FILE_APPEND); + file_put_contents(RASPAP_IP6TABLES_SCRIPT, $txt, FILE_APPEND); if ( empty($conf) || empty($ipt) ) return false; $count=0; foreach ( $ipt["order"] as $idx ) { @@ -86,7 +100,8 @@ function configureFirewall() { if ( isRuleEnabled($sect, $conf) ) { $str_rules= createRuleStr($sect, $conf); if ( !empty($str_rules) ) { - file_put_contents(RASPAP_IPTABLES_SCRIPT, $str_rules, FILE_APPEND); + if ( isIPv4($sect) ) file_put_contents(RASPAP_IPTABLES_SCRIPT, $str_rules, FILE_APPEND); + if ( isIPv6($sect) ) file_put_contents(RASPAP_IP6TABLES_SCRIPT, $str_rules, FILE_APPEND); ++$count; } } @@ -98,6 +113,10 @@ function configureFirewall() { exec("sudo ".RASPAP_IPTABLES_SCRIPT); // exec("sudo iptables-save > /etc/iptables/rules.v4"); // unlink(RASPAP_IPTABLES_SCRIPT); + exec("chmod +x ".RASPAP_IP6TABLES_SCRIPT); + exec("sudo ".RASPAP_IP6TABLES_SCRIPT); +// exec("sudo iptables-save > /etc/iptables/rules.v6"); +// unlink(RASPAP_IP6TABLES_SCRIPT); } return ($count > 0); } From 882535b1309fc4fe8fcc2e29502c0bd8ed600559 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Sun, 25 Jul 2021 17:27:31 +0200 Subject: [PATCH 244/300] Get VPN state from active tun/wg device --- includes/firewall.php | 37 ++++++++++++++++++++++++++++--------- installers/raspap.sudoers | 1 + 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/includes/firewall.php b/includes/firewall.php index 1b7636229..d66998668 100644 --- a/includes/firewall.php +++ b/includes/firewall.php @@ -142,28 +142,47 @@ function ReadFirewallConf() { $conf["client-device"] = ""; $conf["restricted-ips"] = ""; } + exec('ifconfig | grep -E -i "tun+"', $ret); + $conf["openvpn-enable"] = !empty($ret); + unset($ret); + exec('ifconfig | grep -E -i "wg+"', $ret); + $conf["wireguard-enable"] = !empty($ret); return $conf; } function getVPN_IPs() { $ips = ""; - # get openvpn server IPs for UDP (if existing) + # get openvpn and wireguard server IPs if ( RASPI_OPENVPN_ENABLED && ($fconf = glob(RASPI_OPENVPN_CLIENT_PATH ."/*.conf")) !== false && !empty($fconf) ) { foreach ( $fconf as $f ) { - unset($result); - exec('cat '.$f.' | sed -rn "s/^remote\s*([a-z0-9\.\-\_]*)\s*([0-9]*).*$/\1/ip" ', $result); - $ip = (isset($result[0])) ? $result[0] : ""; - unset($result); - exec('cat '.$f.' | sed -rn "s/^proto\s*([a-z]*).*$/\1/ip" ', $result); - $proto = (isset($result[0])) ? $result[0] : ""; - if ( !empty($ip) && trim(strtolower($proto)) === "udp" ) { + unset($result); + exec('cat '.$f.' | sed -rn "s/^remote\s*([a-z0-9\.\-\_]*)\s*([0-9]*).*$/\1 \2/ip" ', $result); + if ( !empty($result) ) { + $result = explode(" ",$result[0]); + $ip = (isset($result[0])) ? $result[0] : ""; + $port = (isset($result[1])) ? $result[1] : ""; + if ( !empty($ip) ) { $ip = gethostbyname($ip); if ( filter_var($ip,FILTER_VALIDATE_IP) && strpos($ips, $ip) === false ) $ips .= " $ip"; + } } } } - # get wireguard server IPs for UDP (if existing) + # get wireguard server IPs if ( RASPI_WIREGUARD_ENABLED && ($fconf = glob(RASPI_WIREGUARD_PATH ."/*.conf")) !== false && !empty($fconf) ) { + foreach ( $fconf as $f ) { + unset($result); + exec('sudo /bin/cat '.$f.' | sed -rn "s/^endpoint\s*=\s*([a-z0-9\.\-\_]*:[0-9]*).*$/\1/ip" ', $result); + if ( !empty($result) ) { + $result = explode(":",$result[0]); + $ip = (isset($result[0])) ? $result[0] : ""; + $port = (isset($result[1])) ? $result[1] : ""; + if ( !empty($ip) ) { + $ip = gethostbyname($ip); + if ( filter_var($ip,FILTER_VALIDATE_IP) && strpos($ips, $ip) === false ) $ips .= " $ip"; + } + } + } } return trim($ips); } diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index ae7379106..b85fe487a 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -63,3 +63,4 @@ 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 www-data ALL=(ALL) NOPASSWD:/tmp/iptables_raspap.sh +www-data ALL=(ALL) NOPASSWD:/tmp/ip6tables_raspap.sh From 088699905527b1f3a7b8879dc249955c0c29b6aa Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Mon, 26 Jul 2021 15:42:14 +0200 Subject: [PATCH 245/300] Improve active VPN detection --- includes/firewall.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/firewall.php b/includes/firewall.php index d66998668..3f4ef8b73 100644 --- a/includes/firewall.php +++ b/includes/firewall.php @@ -142,10 +142,10 @@ function ReadFirewallConf() { $conf["client-device"] = ""; $conf["restricted-ips"] = ""; } - exec('ifconfig | grep -E -i "tun+"', $ret); + exec('ifconfig | grep -E -i "^tun[0-9]"', $ret); $conf["openvpn-enable"] = !empty($ret); unset($ret); - exec('ifconfig | grep -E -i "wg+"', $ret); + exec('ifconfig | grep -E -i "^wg[0-9]"', $ret); $conf["wireguard-enable"] = !empty($ret); return $conf; } From 3d4b710492fdc5ac19195d4e96b759ba6b4f182e Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Tue, 27 Jul 2021 10:09:36 +0200 Subject: [PATCH 246/300] Allow IPv6 addresses for VPN server --- includes/firewall.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/includes/firewall.php b/includes/firewall.php index 3f4ef8b73..9a61f304d 100644 --- a/includes/firewall.php +++ b/includes/firewall.php @@ -142,10 +142,10 @@ function ReadFirewallConf() { $conf["client-device"] = ""; $conf["restricted-ips"] = ""; } - exec('ifconfig | grep -E -i "^tun[0-9]"', $ret); + exec('ifconfig | grep -E -i "tun+"', $ret); $conf["openvpn-enable"] = !empty($ret); unset($ret); - exec('ifconfig | grep -E -i "^wg[0-9]"', $ret); + exec('ifconfig | grep -E -i "wg+"', $ret); $conf["wireguard-enable"] = !empty($ret); return $conf; } @@ -156,7 +156,7 @@ function getVPN_IPs() { if ( RASPI_OPENVPN_ENABLED && ($fconf = glob(RASPI_OPENVPN_CLIENT_PATH ."/*.conf")) !== false && !empty($fconf) ) { foreach ( $fconf as $f ) { unset($result); - exec('cat '.$f.' | sed -rn "s/^remote\s*([a-z0-9\.\-\_]*)\s*([0-9]*).*$/\1 \2/ip" ', $result); + exec('cat '.$f.' | sed -rn "s/^remote\s*([a-z0-9\.\-\_:]*)\s*([0-9]*)\s*$/\1 \2/ip" ', $result); if ( !empty($result) ) { $result = explode(" ",$result[0]); $ip = (isset($result[0])) ? $result[0] : ""; @@ -172,9 +172,9 @@ function getVPN_IPs() { if ( RASPI_WIREGUARD_ENABLED && ($fconf = glob(RASPI_WIREGUARD_PATH ."/*.conf")) !== false && !empty($fconf) ) { foreach ( $fconf as $f ) { unset($result); - exec('sudo /bin/cat '.$f.' | sed -rn "s/^endpoint\s*=\s*([a-z0-9\.\-\_]*:[0-9]*).*$/\1/ip" ', $result); + exec('sudo /bin/cat '.$f.' | sed -rn "s/^endpoint\s*=\s*\[?([a-z0-9\.\-\_:]*)\]?:([0-9]*)\s*$/\1 \2/ip" ', $result); if ( !empty($result) ) { - $result = explode(":",$result[0]); + $result = explode(" ",$result[0]); $ip = (isset($result[0])) ? $result[0] : ""; $port = (isset($result[1])) ? $result[1] : ""; if ( !empty($ip) ) { From f572fdd39e59fc63e7c98fd6006b3d5cda577759 Mon Sep 17 00:00:00 2001 From: Christian Zeitnitz Date: Tue, 27 Jul 2021 11:25:42 +0200 Subject: [PATCH 247/300] Improve search for tun and wg device --- includes/firewall.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/firewall.php b/includes/firewall.php index 9a61f304d..1f05c9fe6 100644 --- a/includes/firewall.php +++ b/includes/firewall.php @@ -142,10 +142,10 @@ function ReadFirewallConf() { $conf["client-device"] = ""; $conf["restricted-ips"] = ""; } - exec('ifconfig | grep -E -i "tun+"', $ret); + exec('ifconfig | grep -E -i "^tun[0-9]"', $ret); $conf["openvpn-enable"] = !empty($ret); unset($ret); - exec('ifconfig | grep -E -i "wg+"', $ret); + exec('ifconfig | grep -E -i "^wg[0-9]"', $ret); $conf["wireguard-enable"] = !empty($ret); return $conf; } From d12aa8da21d593ea262b075f9f583a827d88a6b2 Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Fri, 30 Jul 2021 21:01:29 +0200 Subject: [PATCH 248/300] Update README.md --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0102b431e..3d39834f6 100644 --- a/README.md +++ b/README.md @@ -27,15 +27,17 @@ As part of the initial rollout of Insiders, all previous one-time backers of Ras ## Exclusive features 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](https://docs.raspap.com/openvpn.html#multiple-client-configs) -✅ [OpenVPN certificate authentication](https://docs.raspap.com/openvpn.html#certificate-authentication) +✅ [Manage OpenVPN client configs](https://docs.raspap.com/openvpn/#multiple-client-configs) +✅ [OpenVPN certificate authentication](https://docs.raspap.com/openvpn/#certificate-authentication) ✅ OpenVPN service logging ✅ Night mode toggle ✅ Restrict network to static clients -✅ [WireGuard support](https://docs.raspap.com/wireguard.html) -✅ [Set AP transmit power](https://docs.raspap.com/ap-basics.html#transmit-power) +✅ [WireGuard support](https://docs.raspap.com/wireguard/) +✅ [Set AP transmit power](https://docs.raspap.com/ap-basics/#transmit-power) ✅ Mobile data client support ⚙️ Traffic shaping (in progress) +⚙️ Firewall settings (in progress) +⚙️ Printable WiFi signs (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/RaspAP/raspap-insiders/discussions) and let us know! From b4394513cac908cf14efa35c92d4a5827e422774 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 3 Aug 2021 23:37:42 +0100 Subject: [PATCH 249/300] Update template msg + en_US locale --- locale/en_US/LC_MESSAGES/messages.mo | Bin 28313 -> 28395 bytes locale/en_US/LC_MESSAGES/messages.po | 5 ++++- templates/dhcp/static_leases.php | 6 +++--- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/locale/en_US/LC_MESSAGES/messages.mo b/locale/en_US/LC_MESSAGES/messages.mo index b1d575103df844f9061bbe9f73cf985b650b4813..45eef151276bb949578630c04ffd1b7f061152ec 100644 GIT binary patch delta 8062 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 delta 7980 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

&5y%=BaW%z63A( z@C5vZ^4|Nd|C4+R!rR`{jlBLFl8S#28N@rpBA&rj$y@~K?UB*F-qF-z_BVjN}jxmI61Ve`GQ7tbiYss|Sk zjctW0mCh02|5rCb7f8b$JfE-ddCQj5xj&fv--w@yuZcR;JwS{npG;IIUx0InzZ34~ z_)r>4%q6B1cL=5W#C75-F^UKGqf#OH2z;7|AU}o3Cclu_Oepmtf~Z#-fF(}W`I#jD zJke77|De6_FH9g-5_O5T_Tfw9^N2*sJ&1kemD-U%=wwY6`GMp=C6?K`fz(Bj4w%JPn&(zo{i0I(EES^xk5 diff --git a/locale/en_US/LC_MESSAGES/messages.po b/locale/en_US/LC_MESSAGES/messages.po index c1b9ff474..a28fb4ec6 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: 2021-03-08 09:00+0000\n" +"PO-Revision-Date: 2021-08-04 00:35+0000\n" "Last-Translator: Bill Zimmerman \n" "Language-Team: \n" "Language: en_US\n" @@ -368,6 +368,9 @@ msgstr "Expire time" msgid "MAC Address" msgstr "MAC Address" +msgid "Optional comment" +msgstr "Optional comment" + msgid "Host name" msgstr "Host name" diff --git a/templates/dhcp/static_leases.php b/templates/dhcp/static_leases.php index 531820e7d..a8d371baa 100644 --- a/templates/dhcp/static_leases.php +++ b/templates/dhcp/static_leases.php @@ -19,7 +19,7 @@ " class="form-control">

- " class="form-control"> + " class="form-control">
@@ -36,7 +36,7 @@ " class="form-control">
- " class="form-control"> + " class="form-control">
@@ -67,7 +67,7 @@ " class="form-control">
- " class="form-control"> + " class="form-control">
From f114a701914c6a4ebf3b3254164a4eb7d341d0c3 Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 5 Aug 2021 16:09:20 +0100 Subject: [PATCH 250/300] Update en_US locale w/ firewall msgs --- locale/en_US/LC_MESSAGES/messages.po | 71 ++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/locale/en_US/LC_MESSAGES/messages.po b/locale/en_US/LC_MESSAGES/messages.po index a28fb4ec6..c56747ac4 100644 --- a/locale/en_US/LC_MESSAGES/messages.po +++ b/locale/en_US/LC_MESSAGES/messages.po @@ -1140,3 +1140,74 @@ msgstr "WireGuard configuration updated successfully" msgid "WireGuard configuration failed to be updated" msgstr "WireGuard configuration failed to be updated" +#: templates/firewall.php + +msgid "Client Firewall" +msgstr "Client Firewall" + +msgid "Firewall is ENABLED" +msgstr "Firewall is ENABLED" + +msgid "Firewall is OFF" +msgstr "Firewall is OFF" + +msgid "The default firewall will only allow outgoing and already established traffic." +msgstr "The default firewall will only allow outgoing and already established traffic." + +msgid "No incoming UDP traffic is allowed." +msgstr "No incoming UDP traffic is allowed." + +msgid "There are no restrictions for the access point %s." +msgstr "There are no restrictions for the access point %s." + +msgid "Exception: Service" +msgstr "Exception: Service" + +msgid "allow SSH access on port 22" +msgstr "allow SSH access on port 22" + +msgid "allow access to the RaspAP GUI on port 80 or 443" +msgstr "allow access to the RaspAP GUI on port 80 or 443" + +msgid "Allow incoming connections for some services from the internet side." +msgstr "Allow incoming connections for some services from the internet side." + +msgid "Exception: network device" +msgstr "Exception: network device" + +msgid "Exclude device(s)" +msgstr "Exclude device(s)" + +msgid "Exclude the given network device(s) (separated by a blank or comma) from firewall rules." +msgstr "Exclude the given network device(s) (separated by a blank or comma) from firewall rules." + +msgid "Current client devices: %s" +msgstr "Current client devices: %s" + +msgid "The access point %s is per default excluded." +msgstr "The access point %s is per default excluded." + +msgid "Exception: IP-Address" +msgstr "Exception: IP-Address" + +msgid "Allow incoming connections from" +msgstr "Allow incoming connections from" + +msgid "For the given IP-addresses (separated by a blank or comma) the incoming connection (via TCP and UDP) is accepted." +msgstr "For the given IP-addresses (separated by a blank or comma) the incoming connection (via TCP and UDP) is accepted." + +msgid "This is required for an OpenVPN via UDP or Wireguard connection." +msgstr "This is required for an OpenVPN via UDP or Wireguard connection." + +msgid "The list of configured VPN server IP addresses: %s" +msgstr "The list of configured VPN server IP addresses: %s" + +msgid "Disable Firewall" +msgstr "Disable Firewall" + +msgid "Enable Firewall" +msgstr "Enable Firewall" + +msgid "Apply changes" +msgstr "Apply changes" +: From 9b76fd54c67d61b36393dac8224167d021447b99 Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 5 Aug 2021 16:12:11 +0100 Subject: [PATCH 251/300] Format labels for locale support --- templates/firewall.php | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) mode change 100644 => 100755 templates/firewall.php diff --git a/templates/firewall.php b/templates/firewall.php old mode 100644 new mode 100755 index 478ed652f..e61686d46 --- a/templates/firewall.php +++ b/templates/firewall.php @@ -14,11 +14,17 @@ - +
-

No incoming UDP traffic is allowed.
There are no restrictions for the access point $ap_device.") ?>

+

+ +
+
+ %s."), $ap_device); ?> +
+

@@ -45,7 +51,11 @@ " aria-describedby="exclusion-description" >

- Current client devices: $str_clients
The access point ". $ap_device ." is per default excluded.") ?>
+ +
+ %s"), $str_clients); ?>
+ %s is per default excluded."), $ap_device); ?> +

@@ -55,8 +65,11 @@ " aria-describedby="excl-ips-description" >

- This is required for an OpenVPN via UDP or Wireguard connection.") ?> - The list of configured VPN server IP addresses: ". $vpn_ips. "") ?> + +
+
+ %s"), $vpn_ips); ?> +

From cb2e97fdec63dfc157b237e5643acffd7a4e4cd4 Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 5 Aug 2021 16:12:45 +0100 Subject: [PATCH 252/300] Minor: mode change --- templates/torproxy.php | 0 templates/wireguard.php | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 templates/torproxy.php mode change 100644 => 100755 templates/wireguard.php diff --git a/templates/torproxy.php b/templates/torproxy.php old mode 100644 new mode 100755 diff --git a/templates/wireguard.php b/templates/wireguard.php old mode 100644 new mode 100755 From 99577938f6ce7f30aee2b042a6a1adb2e350d18f Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 5 Aug 2021 18:05:31 +0100 Subject: [PATCH 253/300] Formatting: processed w/ phpcbf --- includes/firewall.php | 375 +++++++++++++++++++++++------------------- 1 file changed, 205 insertions(+), 170 deletions(-) diff --git a/includes/firewall.php b/includes/firewall.php index 1f05c9fe6..63940662f 100644 --- a/includes/firewall.php +++ b/includes/firewall.php @@ -3,82 +3,99 @@ require_once 'includes/status_messages.php'; require_once 'includes/functions.php'; -define('RASPAP_IPTABLES_SCRIPT',"/tmp/iptables_raspap.sh"); -define('RASPAP_IP6TABLES_SCRIPT',"/tmp/ip6tables_raspap.sh"); +define('RASPAP_IPTABLES_SCRIPT', "/tmp/iptables_raspap.sh"); +define('RASPAP_IP6TABLES_SCRIPT', "/tmp/ip6tables_raspap.sh"); -function getDependson(&$rule, &$conf) { - if ( isset($rule["dependson"][0]) ) { - $don = &$rule["dependson"]; - if ( !empty($don[0]) && isset($conf[$don[0]["var"]]) ) { - if ( !isset($don[0]["type"]) ) $don[0]["type"]="bool"; - return $don; - } - } - return false; +function getDependson(&$rule, &$conf) +{ + if (isset($rule["dependson"][0]) ) { + $don = &$rule["dependson"]; + if (!empty($don[0]) && isset($conf[$don[0]["var"]]) ) { + if (!isset($don[0]["type"]) ) { $don[0]["type"]="bool"; + } + return $don; + } + } + return false; } -function isRuleEnabled(&$sect, &$conf) { - $fw_on = isset($conf["firewall-enable"]) && $conf["firewall-enable"]; - $active = isset($sect["fw-state"]) && $sect["fw-state"]==1; - $active = $fw_on ? $active : !$active; - $active = $active || !isset($sect["fw-state"]); - if ( ($don = getDependson($sect, $conf)) !== false && - $don[0]["type"] == "bool" && !$conf[$don[0]["var"]] ) $active = false; - return $active; +function isRuleEnabled(&$sect, &$conf) +{ + $fw_on = isset($conf["firewall-enable"]) && $conf["firewall-enable"]; + $active = isset($sect["fw-state"]) && $sect["fw-state"]==1; + $active = $fw_on ? $active : !$active; + $active = $active || !isset($sect["fw-state"]); + if (($don = getDependson($sect, $conf)) !== false + && $don[0]["type"] == "bool" && !$conf[$don[0]["var"]] + ) { $active = false; + } + return $active; } -function createRuleStr(&$sect, &$conf) { - if ( !is_array($sect["rules"]) ) return ""; - $rules = $sect["rules"]; - $depon = getDependson($sect,$conf); - $rs = array(); - foreach ( $rules as $rule ) { - if ( preg_match('/\$[a-z0-9]*\$/i',$rule) ) { - $r = array($rule); - foreach ( $depon as $dep ) { - $rr = array(); - $repl=$val=""; - switch ( $dep["type"] ) { - case "list": - if ( isset($dep["var"]) && !empty($conf[$dep["var"]]) ) $val = explode(' ', $conf[$dep["var"]]); - if ( !empty($val) && isset($dep["replace"]) ) $repl=$dep["replace"]; - break; - case "string": - if ( isset($dep["var"]) ) $val=$conf[$dep["var"]]; - if ( !empty($val) && isset($dep["replace"]) ) $repl=$dep["replace"]; - break; - default: - break; - } - if ( !empty($repl) && !empty($val) ) { - if ( is_array($val) ) { - foreach ( $val as $v ) $rr = array_merge($rr,str_replace($repl, $v, $r)); - } - else $rr = array_merge($rr, str_replace($repl, $val, $r)); +function createRuleStr(&$sect, &$conf) +{ + if (!is_array($sect["rules"]) ) { return ""; + } + $rules = $sect["rules"]; + $depon = getDependson($sect, $conf); + $rs = array(); + foreach ( $rules as $rule ) { + if (preg_match('/\$[a-z0-9]*\$/i', $rule) ) { + $r = array($rule); + foreach ( $depon as $dep ) { + $rr = array(); + $repl=$val=""; + switch ( $dep["type"] ) { + case "list": + if (isset($dep["var"]) && !empty($conf[$dep["var"]]) ) { $val = explode(' ', $conf[$dep["var"]]); + } + if (!empty($val) && isset($dep["replace"]) ) { $repl=$dep["replace"]; + } + break; + case "string": + if (isset($dep["var"]) ) { $val=$conf[$dep["var"]]; + } + if (!empty($val) && isset($dep["replace"]) ) { $repl=$dep["replace"]; + } + break; + default: + break; + } + if (!empty($repl) && !empty($val) ) { + if (is_array($val) ) { + foreach ( $val as $v ) { $rr = array_merge($rr, str_replace($repl, $v, $r)); + } + } + else { $rr = array_merge($rr, str_replace($repl, $val, $r)); + } + } + $r = !empty($rr) ? $rr : $r; } - $r = !empty($rr) ? $rr : $r; - } - $rs = array_merge($rs,$rr); - } else { - $rs[] = $rule; - } - } - $str=""; - foreach ( $rs as $r ) { - if ( !preg_match('/\$[a-z0-9]*\$/i',$r) ) $str .= '$IPT '.$r."\n"; - } - return $str; + $rs = array_merge($rs, $rr); + } else { + $rs[] = $rule; + } + } + $str=""; + foreach ( $rs as $r ) { + if (!preg_match('/\$[a-z0-9]*\$/i', $r) ) { $str .= '$IPT '.$r."\n"; + } + } + return $str; } -function isIPv4(&$rule) { - return !isset($rule["ip-version"]) || strstr($rule["ip-version"],"4") !== false; +function isIPv4(&$rule) +{ + return !isset($rule["ip-version"]) || strstr($rule["ip-version"], "4") !== false; } -function isIPv6(&$rule) { - return !isset($rule["ip-version"]) || strstr($rule["ip-version"],"6") !== false; +function isIPv6(&$rule) +{ + return !isset($rule["ip-version"]) || strstr($rule["ip-version"], "6") !== false; } -function configureFirewall() { +function configureFirewall() +{ $json = file_get_contents(RASPAP_IPTABLES_CONF); $ipt = json_decode($json, true); $conf = ReadFirewallConf(); @@ -92,55 +109,61 @@ function configureFirewall() { $txt .= "\$IPT -t nat -F\n"; file_put_contents(RASPAP_IPTABLES_SCRIPT, $txt, FILE_APPEND); file_put_contents(RASPAP_IP6TABLES_SCRIPT, $txt, FILE_APPEND); - if ( empty($conf) || empty($ipt) ) return false; + if (empty($conf) || empty($ipt) ) { return false; + } $count=0; foreach ( $ipt["order"] as $idx ) { - if ( isset($ipt[$idx]) ) { - foreach ( $ipt[$idx] as $i => $sect ) { - if ( isRuleEnabled($sect, $conf) ) { - $str_rules= createRuleStr($sect, $conf); - if ( !empty($str_rules) ) { - if ( isIPv4($sect) ) file_put_contents(RASPAP_IPTABLES_SCRIPT, $str_rules, FILE_APPEND); - if ( isIPv6($sect) ) file_put_contents(RASPAP_IP6TABLES_SCRIPT, $str_rules, FILE_APPEND); - ++$count; - } - } - } - } + if (isset($ipt[$idx]) ) { + foreach ( $ipt[$idx] as $i => $sect ) { + if (isRuleEnabled($sect, $conf) ) { + $str_rules= createRuleStr($sect, $conf); + if (!empty($str_rules) ) { + if (isIPv4($sect) ) { file_put_contents(RASPAP_IPTABLES_SCRIPT, $str_rules, FILE_APPEND); + } + if (isIPv6($sect) ) { file_put_contents(RASPAP_IP6TABLES_SCRIPT, $str_rules, FILE_APPEND); + } + ++$count; + } + } + } + } } - if ( $count > 0 ) { - exec("chmod +x ".RASPAP_IPTABLES_SCRIPT); - exec("sudo ".RASPAP_IPTABLES_SCRIPT); -// exec("sudo iptables-save > /etc/iptables/rules.v4"); -// unlink(RASPAP_IPTABLES_SCRIPT); - exec("chmod +x ".RASPAP_IP6TABLES_SCRIPT); - exec("sudo ".RASPAP_IP6TABLES_SCRIPT); -// exec("sudo iptables-save > /etc/iptables/rules.v6"); -// unlink(RASPAP_IP6TABLES_SCRIPT); + if ($count > 0 ) { + exec("chmod +x ".RASPAP_IPTABLES_SCRIPT); + exec("sudo ".RASPAP_IPTABLES_SCRIPT); + // exec("sudo iptables-save > /etc/iptables/rules.v4"); + // unlink(RASPAP_IPTABLES_SCRIPT); + exec("chmod +x ".RASPAP_IP6TABLES_SCRIPT); + exec("sudo ".RASPAP_IP6TABLES_SCRIPT); + // exec("sudo iptables-save > /etc/iptables/rules.v6"); + // unlink(RASPAP_IP6TABLES_SCRIPT); } return ($count > 0); } -function WriteFirewallConf($conf) { +function WriteFirewallConf($conf) +{ $ret = false; - if ( is_array($conf) ) write_php_ini($conf,RASPAP_FIREWALL_CONF); + if (is_array($conf) ) { write_php_ini($conf, RASPAP_FIREWALL_CONF); + } return $ret; } -function ReadFirewallConf() { - if ( file_exists(RASPAP_FIREWALL_CONF) ) { - $conf = parse_ini_file(RASPAP_FIREWALL_CONF); +function ReadFirewallConf() +{ + if (file_exists(RASPAP_FIREWALL_CONF) ) { + $conf = parse_ini_file(RASPAP_FIREWALL_CONF); } else { - $conf = array(); - $conf["firewall-enable"] = false; - $conf["ssh-enable"] = false; - $conf["http-enable"] = false; - $conf["excl-devices"] = ""; - $conf["excluded-ips"] = ""; - $conf["ap-device"] = ""; - $conf["client-device"] = ""; - $conf["restricted-ips"] = ""; + $conf = array(); + $conf["firewall-enable"] = false; + $conf["ssh-enable"] = false; + $conf["http-enable"] = false; + $conf["excl-devices"] = ""; + $conf["excluded-ips"] = ""; + $conf["ap-device"] = ""; + $conf["client-device"] = ""; + $conf["restricted-ips"] = ""; } exec('ifconfig | grep -E -i "^tun[0-9]"', $ret); $conf["openvpn-enable"] = !empty($ret); @@ -150,39 +173,42 @@ function ReadFirewallConf() { return $conf; } -function getVPN_IPs() { +function getVPN_IPs() +{ $ips = ""; - # get openvpn and wireguard server IPs - if ( RASPI_OPENVPN_ENABLED && ($fconf = glob(RASPI_OPENVPN_CLIENT_PATH ."/*.conf")) !== false && !empty($fconf) ) { - foreach ( $fconf as $f ) { - unset($result); - exec('cat '.$f.' | sed -rn "s/^remote\s*([a-z0-9\.\-\_:]*)\s*([0-9]*)\s*$/\1 \2/ip" ', $result); - if ( !empty($result) ) { - $result = explode(" ",$result[0]); - $ip = (isset($result[0])) ? $result[0] : ""; - $port = (isset($result[1])) ? $result[1] : ""; - if ( !empty($ip) ) { - $ip = gethostbyname($ip); - if ( filter_var($ip,FILTER_VALIDATE_IP) && strpos($ips, $ip) === false ) $ips .= " $ip"; - } + // get openvpn and wireguard server IPs + if (RASPI_OPENVPN_ENABLED && ($fconf = glob(RASPI_OPENVPN_CLIENT_PATH ."/*.conf")) !== false && !empty($fconf) ) { + foreach ( $fconf as $f ) { + unset($result); + exec('cat '.$f.' | sed -rn "s/^remote\s*([a-z0-9\.\-\_:]*)\s*([0-9]*)\s*$/\1 \2/ip" ', $result); + if (!empty($result) ) { + $result = explode(" ", $result[0]); + $ip = (isset($result[0])) ? $result[0] : ""; + $port = (isset($result[1])) ? $result[1] : ""; + if (!empty($ip) ) { + $ip = gethostbyname($ip); + if (filter_var($ip, FILTER_VALIDATE_IP) && strpos($ips, $ip) === false ) { $ips .= " $ip"; + } + } + } } - } } - # get wireguard server IPs - if ( RASPI_WIREGUARD_ENABLED && ($fconf = glob(RASPI_WIREGUARD_PATH ."/*.conf")) !== false && !empty($fconf) ) { - foreach ( $fconf as $f ) { - unset($result); - exec('sudo /bin/cat '.$f.' | sed -rn "s/^endpoint\s*=\s*\[?([a-z0-9\.\-\_:]*)\]?:([0-9]*)\s*$/\1 \2/ip" ', $result); - if ( !empty($result) ) { - $result = explode(" ",$result[0]); - $ip = (isset($result[0])) ? $result[0] : ""; - $port = (isset($result[1])) ? $result[1] : ""; - if ( !empty($ip) ) { - $ip = gethostbyname($ip); - if ( filter_var($ip,FILTER_VALIDATE_IP) && strpos($ips, $ip) === false ) $ips .= " $ip"; - } + // get wireguard server IPs + if (RASPI_WIREGUARD_ENABLED && ($fconf = glob(RASPI_WIREGUARD_PATH ."/*.conf")) !== false && !empty($fconf) ) { + foreach ( $fconf as $f ) { + unset($result); + exec('sudo /bin/cat '.$f.' | sed -rn "s/^endpoint\s*=\s*\[?([a-z0-9\.\-\_:]*)\]?:([0-9]*)\s*$/\1 \2/ip" ', $result); + if (!empty($result) ) { + $result = explode(" ", $result[0]); + $ip = (isset($result[0])) ? $result[0] : ""; + $port = (isset($result[1])) ? $result[1] : ""; + if (!empty($ip) ) { + $ip = gethostbyname($ip); + if (filter_var($ip, FILTER_VALIDATE_IP) && strpos($ips, $ip) === false ) { $ips .= " $ip"; + } + } + } } - } } return trim($ips); } @@ -200,60 +226,69 @@ function DisplayFirewallConfig() $clients = getClients(); $str_clients = ""; foreach( $clients["device"] as $dev ) { - if ( !$dev["isAP"] ) { - if ( !empty($str_clients) ) $str_clients .= ", "; - $str_clients .= $dev["name"]; - } + if (!$dev["isAP"] ) { + if (!empty($str_clients) ) { $str_clients .= ", "; + } + $str_clients .= $dev["name"]; + } } $fw_conf = ReadFirewallConf(); $fw_conf["ap-device"] = $ap_device; $id=findCurrentClientIndex($clients); - if ( $id >= 0 ) $fw_conf["client-device"] = $clients["device"][$id]["name"]; + if ($id >= 0 ) { $fw_conf["client-device"] = $clients["device"][$id]["name"]; + } if (!empty($_POST)) { $fw_conf["ssh-enable"] = isset($_POST['ssh-enable']); $fw_conf["http-enable"] = isset($_POST['http-enable']); $fw_conf["firewall-enable"] = isset($_POST['firewall-enable']) || isset($_POST['apply-firewall']); - if ( isset($_POST['firewall-enable']) ) $status->addMessage(_('Firewall is now enabled'), 'success'); - if ( isset($_POST['apply-firewall']) ) $status->addMessage(_('Firewall settings changed'), 'success'); - if ( isset($_POST['firewall-disable']) ) $status->addMessage(_('Firewall is now disabled'), 'warning'); - if ( isset($_POST['save-firewall']) ) $status->addMessage(_('Firewall settings saved. Firewall is still disabled.'), 'success'); - if ( isset($_POST['excl-devices']) ) { - $excl = filter_var($_POST['excl-devices'], FILTER_SANITIZE_STRING); - $excl = str_replace(',', ' ', $excl); - $excl = trim(preg_replace('/\s+/', ' ', $excl)); - if ( $fw_conf["excl-devices"] != $excl ) { - $status->addMessage(_('Exclude devices '. $excl), 'success'); - $fw_conf["excl-devices"] = $excl; - } + if (isset($_POST['firewall-enable']) ) { $status->addMessage(_('Firewall is now enabled'), 'success'); + } + if (isset($_POST['apply-firewall']) ) { $status->addMessage(_('Firewall settings changed'), 'success'); + } + if (isset($_POST['firewall-disable']) ) { $status->addMessage(_('Firewall is now disabled'), 'warning'); + } + if (isset($_POST['save-firewall']) ) { $status->addMessage(_('Firewall settings saved. Firewall is still disabled.'), 'success'); } - if ( isset($_POST['excluded-ips']) ) { - $excl = filter_var($_POST['excluded-ips'], FILTER_SANITIZE_STRING); - $excl = str_replace(',', ' ', $excl); - $excl = trim(preg_replace('/\s+/', ' ', $excl)); - if ( !empty($excl) ) { - $excl = explode(' ',$excl); - $str_excl = ""; - foreach ( $excl as $ip ) { - if ( filter_var($ip,FILTER_VALIDATE_IP) ) $str_excl .= "$ip "; - else $status->addMessage(_('Exclude IP address '. $ip . ' failed - not a valid IP address'), 'warning'); - } - } - $str_excl = trim($str_excl); - if ( $fw_conf["excluded-ips"] != $str_excl ) { - $status->addMessage(_('Exclude IP address(es) '. $str_excl ), 'success'); - $fw_conf["excluded-ips"] = $str_excl; - } + if (isset($_POST['excl-devices']) ) { + $excl = filter_var($_POST['excl-devices'], FILTER_SANITIZE_STRING); + $excl = str_replace(',', ' ', $excl); + $excl = trim(preg_replace('/\s+/', ' ', $excl)); + if ($fw_conf["excl-devices"] != $excl ) { + $status->addMessage(_('Exclude devices '. $excl), 'success'); + $fw_conf["excl-devices"] = $excl; + } + } + if (isset($_POST['excluded-ips']) ) { + $excl = filter_var($_POST['excluded-ips'], FILTER_SANITIZE_STRING); + $excl = str_replace(',', ' ', $excl); + $excl = trim(preg_replace('/\s+/', ' ', $excl)); + if (!empty($excl) ) { + $excl = explode(' ', $excl); + $str_excl = ""; + foreach ( $excl as $ip ) { + if (filter_var($ip, FILTER_VALIDATE_IP) ) { $str_excl .= "$ip "; + } else { $status->addMessage(_('Exclude IP address '. $ip . ' failed - not a valid IP address'), 'warning'); + } + } + } + $str_excl = trim($str_excl); + if ($fw_conf["excluded-ips"] != $str_excl ) { + $status->addMessage(_('Exclude IP address(es) '. $str_excl), 'success'); + $fw_conf["excluded-ips"] = $str_excl; + } } WriteFirewallConf($fw_conf); configureFirewall(); } $vpn_ips = getVPN_IPs(); - echo renderTemplate("firewall", compact( - "status", - "ap_device", - "str_clients", - "fw_conf", - "ipt_rules", - "vpn_ips") + echo renderTemplate( + "firewall", compact( + "status", + "ap_device", + "str_clients", + "fw_conf", + "ipt_rules", + "vpn_ips" + ) ); } From 307256d96e39dd6200cd6a4f1c834825682f3f66 Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 5 Aug 2021 18:15:12 +0100 Subject: [PATCH 254/300] Function comment block stubs --- includes/firewall.php | 52 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/includes/firewall.php b/includes/firewall.php index 63940662f..53503ced4 100644 --- a/includes/firewall.php +++ b/includes/firewall.php @@ -6,6 +6,12 @@ define('RASPAP_IPTABLES_SCRIPT', "/tmp/iptables_raspap.sh"); define('RASPAP_IP6TABLES_SCRIPT', "/tmp/ip6tables_raspap.sh"); +/** + * + * @param string $rule + * @param string $conf + * @return string $don + */ function getDependson(&$rule, &$conf) { if (isset($rule["dependson"][0]) ) { @@ -19,6 +25,12 @@ function getDependson(&$rule, &$conf) return false; } +/** + * + * @param string $sect + * @param string $conf + * @return string $active + */ function isRuleEnabled(&$sect, &$conf) { $fw_on = isset($conf["firewall-enable"]) && $conf["firewall-enable"]; @@ -32,6 +44,12 @@ function isRuleEnabled(&$sect, &$conf) return $active; } +/** + * + * @param string $sect + * @param string $conf + * @return string $str + */ function createRuleStr(&$sect, &$conf) { if (!is_array($sect["rules"]) ) { return ""; @@ -84,16 +102,31 @@ function createRuleStr(&$sect, &$conf) return $str; } + +/** + * + * @param string $rule + * @return string boolean + */ function isIPv4(&$rule) { return !isset($rule["ip-version"]) || strstr($rule["ip-version"], "4") !== false; } +/** + * + * @param string $rule + * @return boolean + */ function isIPv6(&$rule) { return !isset($rule["ip-version"]) || strstr($rule["ip-version"], "6") !== false; } +/** + * + * @return string $count + */ function configureFirewall() { $json = file_get_contents(RASPAP_IPTABLES_CONF); @@ -141,6 +174,11 @@ function configureFirewall() return ($count > 0); } +/** + * + * @param string $conf + * @return string $ret + */ function WriteFirewallConf($conf) { $ret = false; @@ -149,7 +187,10 @@ function WriteFirewallConf($conf) return $ret; } - +/** + * + * @return string $conf + */ function ReadFirewallConf() { if (file_exists(RASPAP_FIREWALL_CONF) ) { @@ -173,6 +214,10 @@ function ReadFirewallConf() return $conf; } +/** + * + * @return string $ips + */ function getVPN_IPs() { $ips = ""; @@ -213,7 +258,9 @@ function getVPN_IPs() return trim($ips); } - +/** + * + */ function DisplayFirewallConfig() { @@ -292,3 +339,4 @@ function DisplayFirewallConfig() ) ); } + From 9405297662e8e0cceb1f1dd7b908f01409add4d6 Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 5 Aug 2021 20:29:46 +0100 Subject: [PATCH 255/300] Renamed constant for consistency --- config/config.php | 8 ++++---- includes/firewall.php | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/config/config.php b/config/config.php index 997a3990a..ab3cebb47 100755 --- a/config/config.php +++ b/config/config.php @@ -23,14 +23,14 @@ define('RASPI_OPENVPN_CLIENT_LOGIN', '/etc/openvpn/client/login.conf'); define('RASPI_WIREGUARD_PATH', '/etc/wireguard/'); define('RASPI_WIREGUARD_CONFIG', RASPI_WIREGUARD_PATH.'wg0.conf'); -define('RASPAP_FIREWALL_CONF',"/etc/raspap/networking/firewall/firewall.conf"); -define('RASPAP_IPTABLES_CONF',"/etc/raspap/networking/firewall/iptables_rules.json"); +define('RASPI_FIREWALL_CONF', RASPI_CONFIG.'/networking/firewall/firewall.conf'); +define('RASPI_IPTABLES_CONF', RASPI_CONFIG.'/networking/firewall/iptables_rules.json'); define('RASPI_TORPROXY_CONFIG', '/etc/tor/torrc'); define('RASPI_LIGHTTPD_CONFIG', '/etc/lighttpd/lighttpd.conf'); define('RASPI_ACCESS_CHECK_IP', '1.1.1.1'); define('RASPI_ACCESS_CHECK_DNS', 'one.one.one.one'); -define('RASPI_CLIENT_CONFIG_PATH', '/etc/raspap/networking/client_udev_prototypes.json'); -define('RASPI_MOBILEDATA_CONFIG', '/etc/raspap/networking/mobiledata.ini'); +define('RASPI_CLIENT_CONFIG_PATH', RASPI_CONFIG.'/networking/client_udev_prototypes.json'); +define('RASPI_MOBILEDATA_CONFIG', RASPI_CONFIG.'/networking/mobiledata.ini'); define('RASPI_CLIENT_SCRIPT_PATH', '/usr/local/sbin'); // Constant for the 5GHz wireless regulatory domain diff --git a/includes/firewall.php b/includes/firewall.php index 53503ced4..4fbfad2c2 100644 --- a/includes/firewall.php +++ b/includes/firewall.php @@ -129,7 +129,7 @@ function isIPv6(&$rule) */ function configureFirewall() { - $json = file_get_contents(RASPAP_IPTABLES_CONF); + $json = file_get_contents(RASPI_IPTABLES_CONF); $ipt = json_decode($json, true); $conf = ReadFirewallConf(); $txt = "#!/bin/bash\n"; @@ -182,7 +182,7 @@ function configureFirewall() function WriteFirewallConf($conf) { $ret = false; - if (is_array($conf) ) { write_php_ini($conf, RASPAP_FIREWALL_CONF); + if (is_array($conf) ) { write_php_ini($conf, RASPI_FIREWALL_CONF); } return $ret; } @@ -193,8 +193,8 @@ function WriteFirewallConf($conf) */ function ReadFirewallConf() { - if (file_exists(RASPAP_FIREWALL_CONF) ) { - $conf = parse_ini_file(RASPAP_FIREWALL_CONF); + if (file_exists(RASPI_FIREWALL_CONF) ) { + $conf = parse_ini_file(RASPI_FIREWALL_CONF); } else { $conf = array(); $conf["firewall-enable"] = false; @@ -266,7 +266,7 @@ function DisplayFirewallConfig() $status = new StatusMessages(); - $json = file_get_contents(RASPAP_IPTABLES_CONF); + $json = file_get_contents(RASPI_IPTABLES_CONF); $ipt_rules = json_decode($json, true); getWifiInterface(); $ap_device = $_SESSION['ap_interface']; From ddc5b4455744d16aa4f672dc1e7df3ded702724c Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 5 Aug 2021 22:52:40 +0100 Subject: [PATCH 256/300] Standardize modal dialog --- templates/firewall.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/templates/firewall.php b/templates/firewall.php index e61686d46..702103463 100755 --- a/templates/firewall.php +++ b/templates/firewall.php @@ -77,7 +77,7 @@ " name="apply-firewall" /> " name="firewall-disable" data-toggle="modal" data-target="#firewallModal"/> - " name="save-firewall" /> + " name="save-firewall" /> " name="firewall-enable" data-toggle="modal" data-target="#firewallModal"/> @@ -93,11 +93,18 @@
From 93db1347cbfefb70b566ce0c1c80b4facca74661 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 9 Aug 2021 06:24:29 +0100 Subject: [PATCH 257/300] Instantiate system class w/ namespace --- includes/hostapd.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/hostapd.php b/includes/hostapd.php index 19ed0b08c..386f9925e 100755 --- a/includes/hostapd.php +++ b/includes/hostapd.php @@ -14,7 +14,7 @@ function DisplayHostAPDConfig() { $status = new StatusMessages(); - $system = new System(); + $system = new \RaspAP\System\Sysinfo; $arrConfig = array(); $arr80211Standard = [ 'a' => '802.11a - 5 GHz', From 6be1ad16120d858d418c0108bb47b8d3abc8ad22 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 9 Aug 2021 17:54:20 +0100 Subject: [PATCH 258/300] Legacy 802.11a UI support. Resolves #983 --- app/js/custom.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/js/custom.js b/app/js/custom.js index 09b6176f2..d0eb6e69b 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -365,7 +365,9 @@ function loadChannelSelect(selected) { var countries_5Ghz_max48ch = data["5Ghz_max48ch"].countries; // Map selected hw_mode and country to determine channel list - if (($.inArray(country_code, countries_2_4Ghz_max11ch) !== -1) && (hw_mode !== 'ac') ) { + if (hw_mode === 'a') { + selectablechannels = data["5Ghz_max48ch"].channels; + } else if (($.inArray(country_code, countries_2_4Ghz_max11ch) !== -1) && (hw_mode !== 'ac') ) { selectablechannels = data["2_4GHz_max11ch"].channels; } else if (($.inArray(country_code, countries_2_4Ghz_max14ch) !== -1) && (hw_mode === 'b')) { selectablechannels = data["2_4GHz_max14ch"].channels; From 4df91097faf5bf187118f0c031dfd1204ebf0a30 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Aug 2021 00:15:40 +0000 Subject: [PATCH 259/300] Bump path-parse from 1.0.6 to 1.0.7 Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7. - [Release notes](https://github.com/jbgutierrez/path-parse/releases) - [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7) --- updated-dependencies: - dependency-name: path-parse dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index c30bcf3ae..9ecde7147 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3286,9 +3286,9 @@ path-is-absolute@^1.0.0: integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-root-regex@^0.1.0: version "0.1.2" From 0adbf9f215b1d5f0f1f3ad08fb30de49cd49353b Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Tue, 31 Aug 2021 16:43:44 +0200 Subject: [PATCH 260/300] Fix system info class creation Hostapd.php is throwing an error. Two problems found: 1) the class System is not existing. Its actually called Sysinfo 2) the namespace of the class is RaspAP\System --- includes/hostapd.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/hostapd.php b/includes/hostapd.php index 19ed0b08c..386f9925e 100755 --- a/includes/hostapd.php +++ b/includes/hostapd.php @@ -14,7 +14,7 @@ function DisplayHostAPDConfig() { $status = new StatusMessages(); - $system = new System(); + $system = new \RaspAP\System\Sysinfo; $arrConfig = array(); $arr80211Standard = [ 'a' => '802.11a - 5 GHz', From 7df8f6862114e64522f5a4d025a150ed87698c96 Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Tue, 31 Aug 2021 16:57:46 +0200 Subject: [PATCH 261/300] Fix index of known networks Index now stored in the network array --- templates/wifi_stations.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/templates/wifi_stations.php b/templates/wifi_stations.php index 6949a51a8..9ccfa779b 100755 --- a/templates/wifi_stations.php +++ b/templates/wifi_stations.php @@ -14,12 +14,11 @@
- -

+ @@ -30,6 +29,7 @@

+ @@ -40,6 +40,7 @@

+ From 9aa94a4d228816f152bbf2c66387380eb284d285 Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Tue, 31 Aug 2021 17:00:08 +0200 Subject: [PATCH 262/300] Add network index to network array --- includes/wifi_functions.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/includes/wifi_functions.php b/includes/wifi_functions.php index b7b543dfb..0a24576df 100755 --- a/includes/wifi_functions.php +++ b/includes/wifi_functions.php @@ -6,9 +6,11 @@ function knownWifiStations(&$networks) { // Find currently configured networks exec(' sudo cat ' . RASPI_WPA_SUPPLICANT_CONFIG, $known_return); + $index = 0; foreach ($known_return as $line) { if (preg_match('/network\s*=/', $line)) { - $network = array('visible' => false, 'configured' => true, 'connected' => false); + $network = array('visible' => false, 'configured' => true, 'connected' => false, 'index' => $index); + ++$index; } elseif (isset($network) && $network !== null) { if (preg_match('/^\s*}\s*$/', $line)) { $networks[$ssid] = $network; @@ -98,7 +100,8 @@ function nearbyWifiStations(&$networks, $cached = true) 'channel' => ConvertToChannel($arrNetwork[1]), 'passphrase' => '', 'visible' => true, - 'connected' => false + 'connected' => false, + 'index' => -1 ); } From 433434f4b794b2503c7917cfb6bcff8958fcd411 Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Wed, 18 Aug 2021 20:37:45 +0200 Subject: [PATCH 263/300] Correct non-ASCII SSID read from wpa_supplicant --- includes/wifi_functions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/wifi_functions.php b/includes/wifi_functions.php index b7b543dfb..e29882bcc 100755 --- a/includes/wifi_functions.php +++ b/includes/wifi_functions.php @@ -18,6 +18,7 @@ function knownWifiStations(&$networks) switch (strtolower($lineArr[0])) { case 'ssid': $ssid = trim($lineArr[1], '"'); + $ssid = str_replace('P"','',$ssid); $network['ssid'] = $ssid; break; case 'psk': From 5630258d9a0074c8e1faf282092f0cef5918e662 Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Wed, 18 Aug 2021 21:04:30 +0200 Subject: [PATCH 264/300] Fix SSID name of wifi client --- includes/get_clients.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/get_clients.php b/includes/get_clients.php index 279fbef80..c05ef61b5 100644 --- a/includes/get_clients.php +++ b/includes/get_clients.php @@ -70,7 +70,7 @@ function getClients($simple=true) $cl["device"][$i]["isAP"] = !empty($retiw); unset($retiw); exec("iw dev $dev link 2> /dev/null", $retiw); - if (!$simple && !empty($ssid=preg_only_match("/.*SSID: ([\w ]*).*/", $retiw)) ) { + if (!$simple && !empty($ssid=preg_only_match("/.*SSID:\s*([^\"]*).*/", $retiw)) ) { $cl["device"][$i]["connected"] = "y"; $cl["device"][$i]["ssid"] = $ssid; $cl["device"][$i]["ap-mac"] = preg_only_match("/^Connected to ([0-9a-f\:]*).*$/", $retiw); From d478bf53628185c8ac9c0df4b1fd89b6474ccfd7 Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Wed, 18 Aug 2021 20:24:36 +0200 Subject: [PATCH 265/300] Handle non-ASCII SSID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Non-ASCII SSID has to be stored in wpa_supplicant.conf identical to the string given by wpa_cli scan. This includes the escaped special chars (e.g. ö = \xc3\xb9 ), with a prescript "P". To obtain a valid psk from wpa_passphrase, the UTF8 string is passed (ssid2utf8 replaces the \x bytes by their binary value) to the exec(). For this to work the shell locale has to be UTF8 (via putenv()). Otherwise the string is converted to the shell encoding. --- includes/configure_client.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/includes/configure_client.php b/includes/configure_client.php index 540f0fef0..ddaa4205f 100755 --- a/includes/configure_client.php +++ b/includes/configure_client.php @@ -61,7 +61,7 @@ function DisplayWPAConfig() if (strlen($network['passphrase']) >=8 && strlen($network['passphrase']) <= 63) { unset($wpa_passphrase); unset($line); - exec('wpa_passphrase '.escapeshellarg($ssid). ' ' . escapeshellarg($network['passphrase']), $wpa_passphrase); + exec('wpa_passphrase '. ssid2utf8( escapeshellarg($ssid) ) . ' ' . escapeshellarg($network['passphrase']), $wpa_passphrase); foreach ($wpa_passphrase as $line) { if (preg_match('/^\s*}\s*$/', $line)) { if (array_key_exists('priority', $network)) { @@ -69,7 +69,11 @@ function DisplayWPAConfig() } fwrite($wpa_file, $line.PHP_EOL); } else { - fwrite($wpa_file, $line.PHP_EOL); + if ( strpos($ssid, "\x") !== false && strpos($line, "ssid=\"") !== false ) { + fwrite($wpa_file, "\tssid=P\"".$ssid."\"".PHP_EOL); + } else { + fwrite($wpa_file, $line.PHP_EOL); + } } } } else { From 077a9cd675a3dd949a6da34b49e59d012c4dab4b Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Wed, 18 Aug 2021 21:57:30 +0200 Subject: [PATCH 266/300] Convert non ASCII ssid for display to utf8 Convert hex bytes to binary. Assumes utf8 encoding --- includes/wifi_functions.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/includes/wifi_functions.php b/includes/wifi_functions.php index e29882bcc..b080f10f0 100755 --- a/includes/wifi_functions.php +++ b/includes/wifi_functions.php @@ -181,3 +181,12 @@ function reinitializeWPA($force) return $result; } +/* + * Replace escaped bytes (hex) by binary - assume UTF8 encoding + * + * @param string $ssid + */ +function ssid2utf8($ssid) { + return evalHexSequence($ssid); +} + From 3aa564cdec0318d2f1f275fad8f9db491af923aa Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Tue, 31 Aug 2021 16:18:00 +0200 Subject: [PATCH 267/300] only lower case hex sequences in non-ASCII SSID --- includes/functions.php | 4 ++++ includes/wifi_functions.php | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/includes/functions.php b/includes/functions.php index 8ccd24302..b4774c2f6 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -765,6 +765,10 @@ function evalHexSequence($string) return preg_replace_callback('/\\\x(..)/', $evaluator, $string); } +function hexSequence2lower($string) { + return preg_replace_callback('/\\\\x([0-9A-F]{2})/', function($b){ return '\x'.strtolower($b[1]); }, $string); +} + /* File upload callback object * */ diff --git a/includes/wifi_functions.php b/includes/wifi_functions.php index b080f10f0..0ff1175df 100755 --- a/includes/wifi_functions.php +++ b/includes/wifi_functions.php @@ -73,7 +73,6 @@ function nearbyWifiStations(&$networks, $cached = true) $arrNetwork = preg_split("/[\t]+/", $network); // split result into array $ssid = trim($arrNetwork[4]); - $ssid = evalHexSequence($ssid); // exclude raspap ssid if (empty($ssid) || $ssid == $ap_ssid) { @@ -117,7 +116,7 @@ function connectedWifiStations(&$networks) exec('iwconfig ' .$_SESSION['wifi_client_interface'], $iwconfig_return); foreach ($iwconfig_return as $line) { if (preg_match('/ESSID:\"([^"]+)\"/i', $line, $iwconfig_ssid)) { - $networks[$iwconfig_ssid[1]]['connected'] = true; + $networks[hexSequence2lower($iwconfig_ssid[1])]['connected'] = true; } } } From 7344c323eedde2fc2a82e6d024e4287302cb95af Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Wed, 18 Aug 2021 22:10:33 +0200 Subject: [PATCH 268/300] Fix display of non-ASCII SSID --- ajax/networking/wifi_stations.php | 1 + includes/get_clients.php | 2 ++ templates/dashboard.php | 2 +- templates/wifi_stations/network.php | 2 +- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ajax/networking/wifi_stations.php b/ajax/networking/wifi_stations.php index aca5bc890..ecc9c0986 100644 --- a/ajax/networking/wifi_stations.php +++ b/ajax/networking/wifi_stations.php @@ -14,6 +14,7 @@ nearbyWifiStations($networks, !isset($_REQUEST["refresh"])); connectedWifiStations($networks); sortNetworksByRSSI($networks); +foreach ($networks as $ssid => $network) $networks[$ssid]["ssidutf8"] = ssid2utf8( $ssid ); $connected = array_filter($networks, function($n) { return $n['connected']; } ); $known = array_filter($networks, function($n) { return !$n['connected'] && $n['configured']; } ); diff --git a/includes/get_clients.php b/includes/get_clients.php index c05ef61b5..c167a538a 100644 --- a/includes/get_clients.php +++ b/includes/get_clients.php @@ -1,6 +1,7 @@
-
+
diff --git a/templates/wifi_stations/network.php b/templates/wifi_stations/network.php index e0a9684c6..4d20fb2f0 100644 --- a/templates/wifi_stations/network.php +++ b/templates/wifi_stations/network.php @@ -4,7 +4,7 @@ -
+
From 98fe68e2d38758e15c124ecc5319e8ad302c26dd Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Thu, 2 Sep 2021 10:58:34 +0200 Subject: [PATCH 269/300] Fix numbering of nearby nextworks --- includes/wifi_functions.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/includes/wifi_functions.php b/includes/wifi_functions.php index 0a24576df..7d5decd3a 100755 --- a/includes/wifi_functions.php +++ b/includes/wifi_functions.php @@ -70,6 +70,12 @@ function nearbyWifiStations(&$networks, $cached = true) exec('cat '.RASPI_HOSTAPD_CONFIG.' | sed -rn "s/ssid=(.*)\s*$/\1/p" ', $ap_ssid); $ap_ssid = $ap_ssid[0]; + $index = 0; + if ( !empty($networks) ) { + $lastnet = end($networks); + if ( isset($lastnet['index']) ) $index = $lastnet['index'] + 1; + } + foreach (explode("\n", $scan_results) as $network) { $arrNetwork = preg_split("/[\t]+/", $network); // split result into array @@ -86,8 +92,6 @@ function nearbyWifiStations(&$networks, $cached = true) continue; } - $networks[$ssid]['ssid'] = $ssid; - // If network is saved if (array_key_exists($ssid, $networks)) { $networks[$ssid]['visible'] = true; @@ -95,14 +99,16 @@ function nearbyWifiStations(&$networks, $cached = true) // TODO What if the security has changed? } else { $networks[$ssid] = array( + 'ssid' => $ssid, 'configured' => false, 'protocol' => ConvertToSecurity($arrNetwork[3]), 'channel' => ConvertToChannel($arrNetwork[1]), 'passphrase' => '', 'visible' => true, 'connected' => false, - 'index' => -1 + 'index' => $index ); + ++$index; } // Save RSSI, if the current value is larger than the already stored From a634e0dfaaf612c00f912c1a035986474625878d Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 7 Sep 2021 17:05:58 +0100 Subject: [PATCH 270/300] Merge from upstream master: Pass $upstreamServers to template --- includes/dhcp.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/includes/dhcp.php b/includes/dhcp.php index 9fd7e338c..53a335354 100755 --- a/includes/dhcp.php +++ b/includes/dhcp.php @@ -52,6 +52,7 @@ function DisplayDHCPConfig() exec('cat '. RASPI_DNSMASQ_PREFIX.$ap_iface.'.conf', $return); $conf = array_merge(ParseConfig($return)); $hosts = (array)$conf['dhcp-host']; + $upstreamServers = (array)$conf['server']; exec("ip -o link show | awk -F': ' '{print $2}'", $interfaces); exec('cat ' . RASPI_DNSMASQ_LEASES, $leases); @@ -63,6 +64,7 @@ function DisplayDHCPConfig() "ap_iface", "conf", "hosts", + "upstreamServers", "interfaces", "leases" ) From d07fd0a3279d17d6bf6abfe11c4d89dcece7ebd7 Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Wed, 8 Sep 2021 10:59:58 +0200 Subject: [PATCH 271/300] Implement update firewall function - cleanup firewall.php - add function updateFirewall - add standalone script update_firewall.sh to update the firewall rules --- includes/firewall.php | 80 +++++++++++++++++++++++------------ installers/raspap.sudoers | 4 ++ installers/update_firewall.sh | 29 +++++++++++++ 3 files changed, 86 insertions(+), 27 deletions(-) create mode 100644 installers/update_firewall.sh diff --git a/includes/firewall.php b/includes/firewall.php index 4fbfad2c2..f44833c3c 100644 --- a/includes/firewall.php +++ b/includes/firewall.php @@ -8,9 +8,9 @@ /** * - * @param string $rule - * @param string $conf - * @return string $don + * @param array $rule + * @param array $conf + * @return array $don */ function getDependson(&$rule, &$conf) { @@ -27,9 +27,9 @@ function getDependson(&$rule, &$conf) /** * - * @param string $sect - * @param string $conf - * @return string $active + * @param array $sect + * @param array $conf + * @return boolean $active */ function isRuleEnabled(&$sect, &$conf) { @@ -46,8 +46,8 @@ function isRuleEnabled(&$sect, &$conf) /** * - * @param string $sect - * @param string $conf + * @param array $sect + * @param array $conf * @return string $str */ function createRuleStr(&$sect, &$conf) @@ -105,8 +105,8 @@ function createRuleStr(&$sect, &$conf) /** * - * @param string $rule - * @return string boolean + * @param array $rule + * @return boolean */ function isIPv4(&$rule) { @@ -115,7 +115,7 @@ function isIPv4(&$rule) /** * - * @param string $rule + * @param array $rule * @return boolean */ function isIPv6(&$rule) @@ -125,7 +125,7 @@ function isIPv6(&$rule) /** * - * @return string $count + * @return boolean */ function configureFirewall() { @@ -164,19 +164,19 @@ function configureFirewall() if ($count > 0 ) { exec("chmod +x ".RASPAP_IPTABLES_SCRIPT); exec("sudo ".RASPAP_IPTABLES_SCRIPT); - // exec("sudo iptables-save > /etc/iptables/rules.v4"); - // unlink(RASPAP_IPTABLES_SCRIPT); + exec("sudo iptables-save | sudo tee /etc/iptables/rules.v4"); + unlink(RASPAP_IPTABLES_SCRIPT); exec("chmod +x ".RASPAP_IP6TABLES_SCRIPT); exec("sudo ".RASPAP_IP6TABLES_SCRIPT); - // exec("sudo iptables-save > /etc/iptables/rules.v6"); - // unlink(RASPAP_IP6TABLES_SCRIPT); + exec("sudo ip6tables-save | sudo tee /etc/iptables/rules.v6"); + unlink(RASPAP_IP6TABLES_SCRIPT); } return ($count > 0); } /** * - * @param string $conf + * @param array $conf * @return string $ret */ function WriteFirewallConf($conf) @@ -189,14 +189,15 @@ function WriteFirewallConf($conf) /** * - * @return string $conf + * @return array $conf */ function ReadFirewallConf() { + $conf = array(); if (file_exists(RASPI_FIREWALL_CONF) ) { $conf = parse_ini_file(RASPI_FIREWALL_CONF); - } else { - $conf = array(); + } + if ( !isset($conf["firewall-enable"]) ) { $conf["firewall-enable"] = false; $conf["ssh-enable"] = false; $conf["http-enable"] = false; @@ -260,14 +261,13 @@ function getVPN_IPs() /** * + * @return array $fw_conf */ -function DisplayFirewallConfig() +function getFirewallConfiguration() { - - $status = new StatusMessages(); - + $fw_conf = ReadFirewallConf(); + $json = file_get_contents(RASPI_IPTABLES_CONF); - $ipt_rules = json_decode($json, true); getWifiInterface(); $ap_device = $_SESSION['ap_interface']; $clients = getClients(); @@ -279,11 +279,38 @@ function DisplayFirewallConfig() $str_clients .= $dev["name"]; } } - $fw_conf = ReadFirewallConf(); $fw_conf["ap-device"] = $ap_device; + $fw_conf["client-list"] = $str_clients; $id=findCurrentClientIndex($clients); if ($id >= 0 ) { $fw_conf["client-device"] = $clients["device"][$id]["name"]; } + return $fw_conf; +} + +/** + * + */ +function updateFirewall() +{ + $fw_conf = getFirewallConfiguration(); + if ( isset($fw_conf["firewall-enable"]) ) { + WriteFirewallConf($fw_conf); + configureFirewall(); + } + return; +} + +/** + * + */ +function DisplayFirewallConfig() +{ + $status = new StatusMessages(); + + $fw_conf = getFirewallConfiguration(); + $ap_device = $fw_conf["ap-device"]; + $str_clients = $fw_conf["client-list"]; + if (!empty($_POST)) { $fw_conf["ssh-enable"] = isset($_POST['ssh-enable']); $fw_conf["http-enable"] = isset($_POST['http-enable']); @@ -334,7 +361,6 @@ function DisplayFirewallConfig() "ap_device", "str_clients", "fw_conf", - "ipt_rules", "vpn_ips" ) ); diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index b85fe487a..c7bbac5c0 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -64,3 +64,7 @@ www-data ALL=(ALL) NOPASSWD:/bin/rm /etc/wireguard/*.conf www-data ALL=(ALL) NOPASSWD:/bin/rm /etc/wireguard/wg-*.key www-data ALL=(ALL) NOPASSWD:/tmp/iptables_raspap.sh www-data ALL=(ALL) NOPASSWD:/tmp/ip6tables_raspap.sh +www-data ALL=(ALL) NOPASSWD:/usr/sbin/iptables-save +www-data ALL=(ALL) NOPASSWD:/usr/sbin/ip6tables-save +www-data ALL=(ALL) NOPASSWD:/usr/bin/tee /etc/iptables/rules.v4 +www-data ALL=(ALL) NOPASSWD:/usr/bin/tee /etc/iptables/rules.v6 diff --git a/installers/update_firewall.sh b/installers/update_firewall.sh new file mode 100644 index 000000000..2a7d92124 --- /dev/null +++ b/installers/update_firewall.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# include the raspap helper functions +source /usr/local/sbin/raspap_helpers.sh + +_getWebRoot + +echo -n "Update firewall ... " + +cat << EOF > /tmp/updateFirewall.php + +EOF + +sudo php -d include_path=$raspap_webroot /tmp/updateFirewall.php +rm /tmp/updateFirewall.php +echo "done." From 45bd02ecb7d6341b77175421e837ae5949fb7302 Mon Sep 17 00:00:00 2001 From: zbchristian <33725910+zbchristian@users.noreply.github.com> Date: Wed, 8 Sep 2021 14:28:43 +0200 Subject: [PATCH 272/300] Improve detection of escaped hex bytes in ssid name --- includes/configure_client.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/configure_client.php b/includes/configure_client.php index ddaa4205f..19269025a 100755 --- a/includes/configure_client.php +++ b/includes/configure_client.php @@ -69,7 +69,7 @@ function DisplayWPAConfig() } fwrite($wpa_file, $line.PHP_EOL); } else { - if ( strpos($ssid, "\x") !== false && strpos($line, "ssid=\"") !== false ) { + if ( preg_match('/\\\\x[0-9A-Fa-f]{2}/',$ssid) && strpos($line, "ssid=\"") !== false ) { fwrite($wpa_file, "\tssid=P\"".$ssid."\"".PHP_EOL); } else { fwrite($wpa_file, $line.PHP_EOL); From 84bd1410259bf1e6d02dc950dc327fe470cf96ca Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 4 Nov 2021 09:50:21 +0000 Subject: [PATCH 273/300] Update release version --- README.md | 2 +- includes/defaults.php | 2 +- index.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3d39834f6..3782cda26 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![](https://i.imgur.com/FRU1tXF.png) -[![Release 2.7.5](https://img.shields.io/badge/release-2.7.5-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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/) +[![Release 2.7.6](https://img.shields.io/badge/release-2.7.6-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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. diff --git a/includes/defaults.php b/includes/defaults.php index 026f374a4..458d671ae 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -6,7 +6,7 @@ $defaults = [ 'RASPI_BRAND_TEXT' => 'RaspAP', - 'RASPI_VERSION' => '2.7.5', + 'RASPI_VERSION' => '2.7.6', 'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json', 'RASPI_ADMIN_DETAILS' => RASPI_CONFIG.'/raspap.auth', 'RASPI_WIFI_AP_INTERFACE' => 'wlan0', diff --git a/index.php b/index.php index 3e6e9ff6d..15fae5ca4 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.5 + * @version 2.7.6 * @link https://github.com/raspap/raspap-insiders/ * @link https://raspap.com/ * @see http://sirlagz.net/2013/02/08/raspap-webgui/ From 03d14ed434df1a1ba869d1e6b020dece80b414ee Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 15 Nov 2021 22:14:59 +0000 Subject: [PATCH 274/300] Update for for Debian 11 compatibility --- config/090_raspap.conf | 2 +- installers/common.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config/090_raspap.conf b/config/090_raspap.conf index da01c7515..f44d1a897 100644 --- a/config/090_raspap.conf +++ b/config/090_raspap.conf @@ -1,4 +1,4 @@ # RaspAP default config -log-facility=/tmp/dnsmasq.log +log-facility=/var/log/dnsmasq.log conf-dir=/etc/dnsmasq.d diff --git a/installers/common.sh b/installers/common.sh index e7d02af8a..00d6f513a 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -139,7 +139,7 @@ function _get_linux_distro() { # Sets php package option based on Linux version, abort if unsupported distro function _set_php_package() { case $RELEASE in - 18.04|19.10) # Ubuntu Server + 18.04|19.10|11*) # Ubuntu Server & Debian 11 php_package="php7.4-cgi" phpcgiconf="/etc/php/7.4/cgi/php.ini" ;; 10*) @@ -159,7 +159,7 @@ function _set_php_package() { function _install_dependencies() { _install_log "Installing required packages" _set_php_package - if [ "$php_package" = "php7.4-cgi" ]; then + if [ "$php_package" = "php7.4-cgi" ] && [ ${OS,,} = "ubuntu" ]; then echo "Adding apt-repository ppa:ondrej/php" sudo apt-get install $apt_option software-properties-common || _install_status 1 "Unable to install dependency" sudo add-apt-repository $apt_option ppa:ondrej/php || _install_status 1 "Unable to add-apt-repository ppa:ondrej/php" @@ -609,7 +609,7 @@ function _optimize_php() { if [ "$upgrade" == 0 ]; then _install_log "Optimize PHP configuration" if [ ! -f "$phpcgiconf" ]; then - _install_warning "PHP configuration could not be found." + _install_status 2 "PHP configuration could not be found." return fi From 8cef08cc81851c302d494a412908042adc6cec0c Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 15 Nov 2021 22:15:16 +0000 Subject: [PATCH 275/300] Update release version --- README.md | 2 +- includes/defaults.php | 2 +- index.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3782cda26..524d97599 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![](https://i.imgur.com/FRU1tXF.png) -[![Release 2.7.6](https://img.shields.io/badge/release-2.7.6-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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/) +[![Release 2.7.7](https://img.shields.io/badge/release-2.7.7-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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. diff --git a/includes/defaults.php b/includes/defaults.php index 458d671ae..c098ae555 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -6,7 +6,7 @@ $defaults = [ 'RASPI_BRAND_TEXT' => 'RaspAP', - 'RASPI_VERSION' => '2.7.6', + 'RASPI_VERSION' => '2.7.7', 'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json', 'RASPI_ADMIN_DETAILS' => RASPI_CONFIG.'/raspap.auth', 'RASPI_WIFI_AP_INTERFACE' => 'wlan0', diff --git a/index.php b/index.php index 15fae5ca4..94e72977c 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.6 + * @version 2.7.7 * @link https://github.com/raspap/raspap-insiders/ * @link https://raspap.com/ * @see http://sirlagz.net/2013/02/08/raspap-webgui/ From e75333e29d458b580d3ee86a15f9f3ba27d88fbc Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 17 Nov 2021 18:20:03 +0000 Subject: [PATCH 276/300] Define RASPI_DHCPCD_LOG --- config/config.php | 1 + includes/adblock.php | 2 +- includes/defaults.php | 1 + includes/dhcp.php | 2 +- templates/dhcp/logging.php | 4 ++-- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/config/config.php b/config/config.php index c02ef4426..95b43d419 100755 --- a/config/config.php +++ b/config/config.php @@ -15,6 +15,7 @@ define('RASPI_ADBLOCK_CONFIG', RASPI_DNSMASQ_PREFIX.'adblock.conf'); define('RASPI_HOSTAPD_CONFIG', '/etc/hostapd/hostapd.conf'); define('RASPI_DHCPCD_CONFIG', '/etc/dhcpcd.conf'); +define('RASPI_DHCPCD_LOG', '/var/log/dnsmasq.log'), define('RASPI_WPA_SUPPLICANT_CONFIG', '/etc/wpa_supplicant/wpa_supplicant.conf'); define('RASPI_HOSTAPD_CTRL_INTERFACE', '/var/run/hostapd'); define('RASPI_WPA_CTRL_INTERFACE', '/var/run/wpa_supplicant'); diff --git a/includes/adblock.php b/includes/adblock.php index 034971b71..45983695b 100755 --- a/includes/adblock.php +++ b/includes/adblock.php @@ -78,7 +78,7 @@ function DisplayAdBlockConfig() $adblock_custom_content = file_get_contents(RASPI_ADBLOCK_LISTPATH .'custom.txt'); $adblock_log = ''; - exec('sudo chmod o+r /tmp/dnsmasq.log'); + exec('sudo chmod o+r '.RASPI_DHCPCD_LOG); $handle = fopen("/tmp/dnsmasq.log", "r"); if ($handle) { while (($line = fgets($handle)) !== false) { diff --git a/includes/defaults.php b/includes/defaults.php index c098ae555..ec49636f2 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -20,6 +20,7 @@ 'RASPI_ADBLOCK_CONFIG' => RASPI_DNSMASQ_PREFIX.'adblock.conf', 'RASPI_HOSTAPD_CONFIG' => '/etc/hostapd/hostapd.conf', 'RASPI_DHCPCD_CONFIG' => '/etc/dhcpcd.conf', + 'RASPI_DHCPCD_LOG' => '/var/log/dnsmasq.log', 'RASPI_WPA_SUPPLICANT_CONFIG' => '/etc/wpa_supplicant/wpa_supplicant.conf', 'RASPI_HOSTAPD_CTRL_INTERFACE' => '/var/run/hostapd', 'RASPI_WPA_CTRL_INTERFACE' => '/var/run/wpa_supplicant', diff --git a/includes/dhcp.php b/includes/dhcp.php index 53a335354..3e53017a5 100755 --- a/includes/dhcp.php +++ b/includes/dhcp.php @@ -228,7 +228,7 @@ function updateDnsmasqConfig($iface,$status) // write default 090_raspap.conf $config = '# RaspAP default config'.PHP_EOL; - $config .='log-facility=/tmp/dnsmasq.log'.PHP_EOL; + $config .='log-facility='.RASPI_DHCPCD_LOG.PHP_EOL; $config .='conf-dir=/etc/dnsmasq.d'.PHP_EOL; // handle log option if ($_POST['log-dhcp'] == "1") { diff --git a/templates/dhcp/logging.php b/templates/dhcp/logging.php index ce451e6d2..463075367 100644 --- a/templates/dhcp/logging.php +++ b/templates/dhcp/logging.php @@ -16,8 +16,8 @@
'.htmlspecialchars($log, ENT_QUOTES).''; } else { echo ''; From fc4fe6ea58f948b6de7c76ee8d06d09b1a79338a Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 17 Nov 2021 18:21:06 +0000 Subject: [PATCH 277/300] Update release version --- README.md | 2 +- includes/defaults.php | 2 +- index.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 524d97599..59c008cca 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![](https://i.imgur.com/FRU1tXF.png) -[![Release 2.7.7](https://img.shields.io/badge/release-2.7.7-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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/) +[![Release 2.7.8](https://img.shields.io/badge/release-2.7.8-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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. diff --git a/includes/defaults.php b/includes/defaults.php index ec49636f2..7e4dae7db 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -6,7 +6,7 @@ $defaults = [ 'RASPI_BRAND_TEXT' => 'RaspAP', - 'RASPI_VERSION' => '2.7.7', + 'RASPI_VERSION' => '2.7.8', 'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json', 'RASPI_ADMIN_DETAILS' => RASPI_CONFIG.'/raspap.auth', 'RASPI_WIFI_AP_INTERFACE' => 'wlan0', diff --git a/index.php b/index.php index 94e72977c..c8a1acf27 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.7 + * @version 2.7.8 * @link https://github.com/raspap/raspap-insiders/ * @link https://raspap.com/ * @see http://sirlagz.net/2013/02/08/raspap-webgui/ From c0774a7bde352ad626f8b827118efb9b7170c5f3 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 17 Nov 2021 22:00:20 +0000 Subject: [PATCH 278/300] Minor: typo fix --- config/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.php b/config/config.php index 95b43d419..5c4a298bd 100755 --- a/config/config.php +++ b/config/config.php @@ -15,7 +15,7 @@ define('RASPI_ADBLOCK_CONFIG', RASPI_DNSMASQ_PREFIX.'adblock.conf'); define('RASPI_HOSTAPD_CONFIG', '/etc/hostapd/hostapd.conf'); define('RASPI_DHCPCD_CONFIG', '/etc/dhcpcd.conf'); -define('RASPI_DHCPCD_LOG', '/var/log/dnsmasq.log'), +define('RASPI_DHCPCD_LOG', '/var/log/dnsmasq.log'); define('RASPI_WPA_SUPPLICANT_CONFIG', '/etc/wpa_supplicant/wpa_supplicant.conf'); define('RASPI_HOSTAPD_CTRL_INTERFACE', '/var/run/hostapd'); define('RASPI_WPA_CTRL_INTERFACE', '/var/run/wpa_supplicant'); From 4b27f9c844fe148cf1b80392d284bdbc21fdfd91 Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 18 Nov 2021 10:26:38 +0000 Subject: [PATCH 279/300] Bugfix: vnstat column name change --- ajax/bandwidth/get_bandwidth_hourly.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ajax/bandwidth/get_bandwidth_hourly.php b/ajax/bandwidth/get_bandwidth_hourly.php index bc1c48b58..82c5d9ad4 100644 --- a/ajax/bandwidth/get_bandwidth_hourly.php +++ b/ajax/bandwidth/get_bandwidth_hourly.php @@ -34,16 +34,13 @@ 23 => array('date' => '23:00', 'rx' => 0, 'tx' => 0) ); - - - exec(sprintf('vnstat -i %s --json h', escapeshellarg($interface)), $jsonstdoutvnstat, $exitcodedaily); if ($exitcodedaily !== 0) { exit('vnstat error'); } $jsonobj = json_decode($jsonstdoutvnstat[0], true)['interfaces'][0]; - $jsonData = $jsonobj['traffic']['hours']; + $jsonData = $jsonobj['traffic']['hour']; for ($i = count($jsonData) - 1; $i >= 0; --$i) { $data_template[$jsonData[$i]['id']]['rx'] = round($jsonData[$i]['rx'] / 1024, 0); $data_template[$jsonData[$i]['id']]['tx'] = round($jsonData[$i]['tx'] / 1024, 0); From 83373cf1c8385a777d928a0417770f5cd0e3cc2d Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 18 Nov 2021 16:57:44 +0000 Subject: [PATCH 280/300] Update release version --- README.md | 2 +- includes/defaults.php | 2 +- index.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 59c008cca..d71e25cea 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![](https://i.imgur.com/FRU1tXF.png) -[![Release 2.7.8](https://img.shields.io/badge/release-2.7.8-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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/) +[![Release 2.7.9](https://img.shields.io/badge/release-2.7.9-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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. diff --git a/includes/defaults.php b/includes/defaults.php index 7e4dae7db..3292c407a 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -6,7 +6,7 @@ $defaults = [ 'RASPI_BRAND_TEXT' => 'RaspAP', - 'RASPI_VERSION' => '2.7.8', + 'RASPI_VERSION' => '2.7.9', 'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json', 'RASPI_ADMIN_DETAILS' => RASPI_CONFIG.'/raspap.auth', 'RASPI_WIFI_AP_INTERFACE' => 'wlan0', diff --git a/index.php b/index.php index c8a1acf27..ec6819a16 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.8 + * @version 2.7.9 * @link https://github.com/raspap/raspap-insiders/ * @link https://raspap.com/ * @see http://sirlagz.net/2013/02/08/raspap-webgui/ From 5415707721cf46b2fa33a2d4f87d34defebb6a3b Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 21 Nov 2021 23:24:26 +0000 Subject: [PATCH 281/300] Fix for vnStat v2.6 (bullseye) breaking changes --- ajax/bandwidth/get_bandwidth.php | 4 ++-- ajax/bandwidth/get_bandwidth_hourly.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ajax/bandwidth/get_bandwidth.php b/ajax/bandwidth/get_bandwidth.php index db9ebfe8b..04c1fc03c 100644 --- a/ajax/bandwidth/get_bandwidth.php +++ b/ajax/bandwidth/get_bandwidth.php @@ -42,10 +42,10 @@ $timeunits = filter_input(INPUT_GET, 'tu'); if ($timeunits === 'm') { // months - $jsonData = $jsonobj['interfaces'][0]['traffic']['months']; + $jsonData = $jsonobj['interfaces'][0]['traffic']['month']; } else { // default: days - $jsonData = $jsonobj['interfaces'][0]['traffic']['days']; + $jsonData = $jsonobj['interfaces'][0]['traffic']['day']; } $datasizeunits = filter_input(INPUT_GET, 'dsu'); diff --git a/ajax/bandwidth/get_bandwidth_hourly.php b/ajax/bandwidth/get_bandwidth_hourly.php index 82c5d9ad4..a8e0f5cca 100644 --- a/ajax/bandwidth/get_bandwidth_hourly.php +++ b/ajax/bandwidth/get_bandwidth_hourly.php @@ -42,8 +42,8 @@ $jsonobj = json_decode($jsonstdoutvnstat[0], true)['interfaces'][0]; $jsonData = $jsonobj['traffic']['hour']; for ($i = count($jsonData) - 1; $i >= 0; --$i) { - $data_template[$jsonData[$i]['id']]['rx'] = round($jsonData[$i]['rx'] / 1024, 0); - $data_template[$jsonData[$i]['id']]['tx'] = round($jsonData[$i]['tx'] / 1024, 0); + $data_template[$jsonData[$i]['time']['hour']]['rx'] = round($jsonData[$i]['rx'] / 1024, 0); + $data_template[$jsonData[$i]['time']['hour']]['tx'] = round($jsonData[$i]['tx'] / 1024, 0); } $data = array(); From 4d00bd9e94f73cc422541fe95db4789e6742ef48 Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 25 Nov 2021 08:06:17 +0000 Subject: [PATCH 282/300] Update default getColorOpt() --- includes/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/functions.php b/includes/functions.php index b4774c2f6..b35740233 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -672,7 +672,7 @@ function getThemeOpt() function getColorOpt() { if (!isset($_COOKIE['color'])) { - $color = "#d8224c"; + $color = "#2b8080"; } else { $color = $_COOKIE['color']; } From d12fc09caef8a79e9302e05286a1aca5f364df90 Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 26 Nov 2021 21:38:22 +0000 Subject: [PATCH 283/300] Update nav-link dom element --- index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.php b/index.php index 496fafd4a..5cda40e1e 100755 --- a/index.php +++ b/index.php @@ -180,7 +180,7 @@ From 730c3c3cc8cb7cc16cb4f73f0f1893ff748ffe22 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 28 Nov 2021 09:07:16 +0000 Subject: [PATCH 284/300] Update release version --- README.md | 2 +- includes/defaults.php | 2 +- index.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d71e25cea..9e33764ae 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![](https://i.imgur.com/FRU1tXF.png) -[![Release 2.7.9](https://img.shields.io/badge/release-2.7.9-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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/) +[![Release 2.8.0](https://img.shields.io/badge/release-2.8.0-green)](https://github.com/raspap/raspap-insiders/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Insiders Edition](https://img.shields.io/static/v1?label=Insiders%20Edition&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/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. diff --git a/includes/defaults.php b/includes/defaults.php index 3292c407a..3ba8d848e 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -6,7 +6,7 @@ $defaults = [ 'RASPI_BRAND_TEXT' => 'RaspAP', - 'RASPI_VERSION' => '2.7.9', + 'RASPI_VERSION' => '2.8.0', 'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json', 'RASPI_ADMIN_DETAILS' => RASPI_CONFIG.'/raspap.auth', 'RASPI_WIFI_AP_INTERFACE' => 'wlan0', diff --git a/index.php b/index.php index 06f0b45ae..00454c542 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.9 + * @version 2.8.0 * @link https://github.com/raspap/raspap-insiders/ * @link https://raspap.com/ * @see http://sirlagz.net/2013/02/08/raspap-webgui/ From bf5de0a81d38886af053f84677d8189c31a6a629 Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Sun, 28 Nov 2021 11:39:04 +0100 Subject: [PATCH 285/300] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9e33764ae..4f872919c 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,8 @@ The following features are currently available exclusively to sponsors. A tangib ✅ [WireGuard support](https://docs.raspap.com/wireguard/) ✅ [Set AP transmit power](https://docs.raspap.com/ap-basics/#transmit-power) ✅ Mobile data client support +✅ Firewall settings ⚙️ Traffic shaping (in progress) -⚙️ Firewall settings (in progress) ⚙️ Printable WiFi signs (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/RaspAP/raspap-insiders/discussions) and let us know! From d75175848fa2b6fa6a11619f8ffa4fa40c935bd7 Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Wed, 29 Dec 2021 12:39:59 +0100 Subject: [PATCH 286/300] Fix for issue RaspAP/raspap-webgui#1016 --- includes/dhcp.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/includes/dhcp.php b/includes/dhcp.php index 3e53017a5..850d5847a 100755 --- a/includes/dhcp.php +++ b/includes/dhcp.php @@ -180,9 +180,7 @@ function compareIPs($ip1, $ip2) function updateDnsmasqConfig($iface,$status) { $config = '# RaspAP '.$iface.' configuration'.PHP_EOL; - $config .= 'interface='.$iface.PHP_EOL. - 'dhcp-range='.$_POST['RangeStart'].','.$_POST['RangeEnd']. - ',255.255.255.0,'; + $config .= 'interface='.$iface.PHP_EOL.'dhcp-range='.$_POST['RangeStart'].','.$_POST['RangeEnd'].','.$_POST['SubnetMask'].','; if ($_POST['RangeLeaseTimeUnits'] !== 'infinite') { $config .= $_POST['RangeLeaseTime']; } From 2ca5f25dff321d0f47008ef57a9d42becfd3241d Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 25 Nov 2021 09:02:59 +0000 Subject: [PATCH 287/300] Merge First Edition of RaspAP/raspap-insiders --- app/css/lightsout.css | 2 +- app/img/wifi-qr-code.php | 13 +++ app/js/custom.js | 24 +----- config/config.php | 6 -- includes/adblock.php | 2 +- includes/dashboard.php | 170 +++++++++++++++++++++----------------- includes/defaults.php | 2 +- includes/dhcp.php | 4 +- includes/functions.php | 8 +- includes/networking.php | 12 +-- index.php | 14 +--- installers/common.sh | 6 +- installers/raspap.sudoers | 7 +- templates/dashboard.php | 166 +++++++++++-------------------------- templates/networking.php | 120 +-------------------------- 15 files changed, 177 insertions(+), 379 deletions(-) diff --git a/app/css/lightsout.css b/app/css/lightsout.css index 19ca7c5a7..aa42b7201 100644 --- a/app/css/lightsout.css +++ b/app/css/lightsout.css @@ -378,7 +378,7 @@ tspan, rect { fill: #d2d2d2; } -text-muted { +.text-muted { font-size: 0.8rem; } diff --git a/app/img/wifi-qr-code.php b/app/img/wifi-qr-code.php index e2baf69bd..a36966c33 100644 --- a/app/img/wifi-qr-code.php +++ b/app/img/wifi-qr-code.php @@ -10,12 +10,25 @@ exit; } +function qr_encode($str) +{ + return preg_replace('/(?showMessages(); return; } - - // ------------------------- Button pressed to switch client on/off --------------------------------------------------------- - $switchedOn = false; - if (!RASPI_MONITOR_ENABLED) { - if (isset($_POST['ifdown_wlan0'])) { - // Pressed stop button - $status->addMessage(sprintf(_('Interface is going %s.'), _('down')), 'warning'); - setClientState("down"); - $status->addMessage(sprintf(_('Interface is now %s.'), _('down')), 'success'); - } elseif (isset($_POST['ifup_wlan0'])) { - // Pressed start button - $status->addMessage(sprintf(_('Interface is going %s.'), _('up')), 'warning'); - setClientState("up"); - $status->addMessage(sprintf(_('Interface is now %s.'), _('up')), 'success'); - $switchedOn = true; - } - } - - - // ----------------------------- INFOS ABOUT THE ACCESS POINT ------------------------------------------------------------- - exec('ip a show '.$_SESSION['ap_interface'], $stdoutIp); $stdoutIpAllLinesGlued = implode(" ", $stdoutIp); $stdoutIpWRepeatedSpaces = preg_replace('/\s\s+/', ' ', $stdoutIpAllLinesGlued); @@ -110,40 +88,103 @@ function DisplayDashboard(&$extraFooterScripts) $strTxBytes .= getHumanReadableDatasize($strTxBytes); } - // ------------------------ INFOS ABOUT THE CLIENT--------------------------------------------------------------- - $clientinfo=array("name"=>"none","type"=>-1,"connected"=>"n"); - $raspi_client=$_SESSION['wifi_client_interface']; - loadClientConfig(); - $all_clients = getClients(false); - $clientinfo = array("name" => "none", "connected" => "n"); - if ( ($idx = findCurrentClientIndex($all_clients)) >= 0) $clientinfo = $all_clients["device"][$idx]; - if ($clientinfo["name"] != "none") $raspi_client = $clientinfo["name"]; - $interfaceState = $clientinfo["connected"] == "y" ? 'UP' : 'DOWN'; - $txPower=""; - if ($clientinfo["type"] == "wlan") { - // txpower is now displayed on iw dev(..) info command, not on link command. - exec('iw dev '.$clientinfo["name"].' info | sed -rn "s/.*txpower ([0-9]*)[0-9\.]*( dBm).*/\1\2/p"', $stdoutIwInfo); - if (!empty($stdoutIwInfo)) $txPower=$stdoutIwInfo[0]; + define('SSIDMAXLEN', 32); + // Warning iw comes with: "Do NOT screenscrape this tool, we don't consider its output stable." + exec('iw dev ' .$_SESSION['wifi_client_interface']. ' link ', $stdoutIw); + $stdoutIwAllLinesGlued = implode('+', $stdoutIw); // Break lines with character illegal in SSID and MAC addr + $stdoutIwWRepSpaces = preg_replace('/\s\s+/', ' ', $stdoutIwAllLinesGlued); + + preg_match('/Connected to (([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2}))/', $stdoutIwWRepSpaces, $matchesBSSID) || $matchesBSSID[1] = ''; + $connectedBSSID = $matchesBSSID[1]; + $connectedBSSID = empty($connectedBSSID) ? "-" : $connectedBSSID; + + $wlanHasLink = false; + if ($interfaceState === 'UP') { + $wlanHasLink = true; + } + + if (!preg_match('/SSID: ([^+]{1,'.SSIDMAXLEN.'})/', $stdoutIwWRepSpaces, $matchesSSID)) { + $wlanHasLink = false; + $matchesSSID[1] = 'None'; } - + + $connectedSSID = $matchesSSID[1]; + + preg_match('/freq: (\d+)/i', $stdoutIwWRepSpaces, $matchesFrequency) || $matchesFrequency[1] = ''; + $frequency = $matchesFrequency[1].' MHz'; + + preg_match('/signal: (-?[0-9]+ dBm)/i', $stdoutIwWRepSpaces, $matchesSignal) || $matchesSignal[1] = ''; + $signalLevel = $matchesSignal[1]; + $signalLevel = empty($signalLevel) ? "-" : $signalLevel; + + preg_match('/tx bitrate: ([0-9\.]+ [KMGT]?Bit\/s)/', $stdoutIwWRepSpaces, $matchesBitrate) || $matchesBitrate[1] = ''; + $bitrate = $matchesBitrate[1]; + $bitrate = empty($bitrate) ? "-" : $bitrate; + + // txpower is now displayed on iw dev(..) info command, not on link command. + exec('iw dev '.$_SESSION['wifi_client_interface'].' info ', $stdoutIwInfo); + $stdoutIwInfoAllLinesGlued = implode(' ', $stdoutIwInfo); + $stdoutIpInfoWRepSpaces = preg_replace('/\s\s+/', ' ', $stdoutIwInfoAllLinesGlued); + + preg_match('/txpower ([0-9\.]+ dBm)/i', $stdoutIpInfoWRepSpaces, $matchesTxPower) || $matchesTxPower[1] = ''; + $txPower = $matchesTxPower[1]; + + // iw does not have the "Link Quality". This is a is an aggregate value, + // and depends on the driver and hardware. + // Display link quality as signal quality for now. + $strLinkQuality = 0; + if ($signalLevel > -100 && $wlanHasLink) { + if ($signalLevel >= 0) { + $strLinkQuality = 100; + } else { + $strLinkQuality = 100 + intval($signalLevel); + } + } + + $wlan0up = false; $classMsgDevicestatus = 'warning'; if ($interfaceState === 'UP') { + $wlan0up = true; $classMsgDevicestatus = 'success'; } - if ($switchedOn) exec('sudo ip -s a f label ' . $raspi_client); - + if (!RASPI_MONITOR_ENABLED) { + if (isset($_POST['ifdown_wlan0'])) { + // Pressed stop button + if ($interfaceState === 'UP') { + $status->addMessage(sprintf(_('Interface is going %s.'), _('down')), 'warning'); + exec('sudo ip link set '.$_SESSION['wifi_client_interface'].' down'); + $wlan0up = false; + $status->addMessage(sprintf(_('Interface is now %s.'), _('down')), 'success'); + } elseif ($interfaceState === 'unknown') { + $status->addMessage(_('Interface state unknown.'), 'danger'); + } else { + $status->addMessage(sprintf(_('Interface already %s.'), _('down')), 'warning'); + } + } elseif (isset($_POST['ifup_wlan0'])) { + // Pressed start button + if ($interfaceState === 'DOWN') { + $status->addMessage(sprintf(_('Interface is going %s.'), _('up')), 'warning'); + exec('sudo ip link set ' .$_SESSION['wifi_client_interface']. ' up'); + exec('sudo ip -s a f label ' . $_SESSION['wifi_client_interface']); + $wlan0up = true; + $status->addMessage(sprintf(_('Interface is now %s.'), _('up')), 'success'); + } elseif ($interfaceState === 'unknown') { + $status->addMessage(_('Interface state unknown.'), 'danger'); + } else { + $status->addMessage(sprintf(_('Interface already %s.'), _('up')), 'warning'); + } + } else { + $status->addMessage(sprintf(_('Interface is %s.'), strtolower($interfaceState)), $classMsgDevicestatus); + } + } // brought in from template $arrHostapdConf = parse_ini_file(RASPI_CONFIG.'/hostapd.ini'); $bridgedEnable = $arrHostapdConf['BridgedEnable']; - if ($arrHostapdConf['WifiAPEnable'] == 1) { - $client_interface = 'uap0'; - } else { - $client_interface = $clientinfo["name"]; - } + $clientInterface = $_SESSION['wifi_client_interface']; $apInterface = $_SESSION['ap_interface']; - $clientInterface = $raspi_client; $MACPattern = '"([[:xdigit:]]{2}:){5}[[:xdigit:]]{2}"'; + if (getBridgedState()) { $moreLink = "hostapd_conf"; exec('iw dev ' . $apInterface . ' station dump | grep -oE ' . $MACPattern, $clients); @@ -151,39 +192,11 @@ function DisplayDashboard(&$extraFooterScripts) $moreLink = "dhcpd_conf"; exec('cat ' . RASPI_DNSMASQ_LEASES . '| grep -E $(iw dev ' . $apInterface . ' station dump | grep -oE ' . $MACPattern . ' | paste -sd "|")', $clients); } - $ifaceStatus = $clientinfo["connected"]=="y" ? "up" : "down"; - $isClientConfigured = true; - switch($clientinfo["type"]) { - case "eth": - case "usb": - $client_title = "Client: Ethernet cable"; - $type_name = "Ethernet"; - break; - case "phone": - $client_title = "Client: Smartphone (USB tethering)"; - $type_name = "Smartphone"; - break; - case "wlan": - $client_title = "Wireless Client"; - $type_name = "Wifi"; - break; - case "ppp": - case "hilink": - $client_title = "Mobile Data Client"; - $type_name = "Mobile Data"; - break; - default: - $client_title = "No information available"; - $type_name = "Not configured"; - $ifaceStatus = "warn"; - $isClientConfigured = false; - } + $ifaceStatus = $wlan0up ? "up" : "down"; echo renderTemplate( "dashboard", compact( "clients", - "client_title", - "type_name", "moreLink", "apInterface", "clientInterface", @@ -198,9 +211,14 @@ function DisplayDashboard(&$extraFooterScripts) "strRxBytes", "strTxPackets", "strTxBytes", + "connectedSSID", + "connectedBSSID", + "bitrate", + "signalLevel", "txPower", - "clientinfo", - "isClientConfigured" + "frequency", + "strLinkQuality", + "wlan0up" ) ); $extraFooterScripts[] = array('src'=>'app/js/dashboardchart.js', 'defer'=>false); diff --git a/includes/defaults.php b/includes/defaults.php index 3ba8d848e..52b272762 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -6,7 +6,7 @@ $defaults = [ 'RASPI_BRAND_TEXT' => 'RaspAP', - 'RASPI_VERSION' => '2.8.0', + 'RASPI_VERSION' => '2.7.1', 'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json', 'RASPI_ADMIN_DETAILS' => RASPI_CONFIG.'/raspap.auth', 'RASPI_WIFI_AP_INTERFACE' => 'wlan0', diff --git a/includes/dhcp.php b/includes/dhcp.php index 3e53017a5..850d5847a 100755 --- a/includes/dhcp.php +++ b/includes/dhcp.php @@ -180,9 +180,7 @@ function compareIPs($ip1, $ip2) function updateDnsmasqConfig($iface,$status) { $config = '# RaspAP '.$iface.' configuration'.PHP_EOL; - $config .= 'interface='.$iface.PHP_EOL. - 'dhcp-range='.$_POST['RangeStart'].','.$_POST['RangeEnd']. - ',255.255.255.0,'; + $config .= 'interface='.$iface.PHP_EOL.'dhcp-range='.$_POST['RangeStart'].','.$_POST['RangeEnd'].','.$_POST['SubnetMask'].','; if ($_POST['RangeLeaseTimeUnits'] !== 'infinite') { $config .= $_POST['RangeLeaseTime']; } diff --git a/includes/functions.php b/includes/functions.php index b35740233..81e642ed3 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -757,8 +757,12 @@ function qr_encode($str) return preg_replace('/(? \ No newline at end of file diff --git a/index.php b/index.php index 00454c542..994aba5b3 100755 --- a/index.php +++ b/index.php @@ -14,8 +14,8 @@ * @author Lawrence Yau * @author Bill Zimmerman * @license GNU General Public License, version 3 (GPL-3.0) - * @version 2.8.0 - * @link https://github.com/raspap/raspap-insiders/ + * @version 2.7.1 + * @link https://github.com/raspap/raspap-webgui/ * @link https://raspap.com/ * @see http://sirlagz.net/2013/02/08/raspap-webgui/ * @@ -41,7 +41,6 @@ require_once 'includes/sysstats.php'; require_once 'includes/configure_client.php'; require_once 'includes/networking.php'; -require_once 'includes/firewall.php'; require_once 'includes/themes.php'; require_once 'includes/data_usage.php'; require_once 'includes/about.php'; @@ -176,11 +175,6 @@ - - - @@ -280,8 +274,8 @@ case "/torproxy_conf": DisplayTorProxyConfig(); break; - case "/firewall_conf": - DisplayFirewallConfig(); + case "/torproxy_conf": + DisplayTorProxyConfig(); break; case "/auth_conf": DisplayAuthConfig($config['admin_user'], $config['admin_pass']); diff --git a/installers/common.sh b/installers/common.sh index 00d6f513a..2f10867c0 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -139,10 +139,10 @@ function _get_linux_distro() { # Sets php package option based on Linux version, abort if unsupported distro function _set_php_package() { case $RELEASE in - 18.04|19.10|11*) # Ubuntu Server & Debian 11 + 18.04|19.10|11*) # Ubuntu Server & Debian 11 php_package="php7.4-cgi" phpcgiconf="/etc/php/7.4/cgi/php.ini" ;; - 10*) + 10*|11*) php_package="php7.3-cgi" phpcgiconf="/etc/php/7.3/cgi/php.ini" ;; 9*) @@ -159,7 +159,7 @@ function _set_php_package() { function _install_dependencies() { _install_log "Installing required packages" _set_php_package - if [ "$php_package" = "php7.4-cgi" ] && [ ${OS,,} = "ubuntu" ]; then + if [ "$php_package" = "php7.4-cgi" ] && [ ${OS,,} = "ubuntu" ]; then echo "Adding apt-repository ppa:ondrej/php" sudo apt-get install $apt_option software-properties-common || _install_status 1 "Unable to install dependency" sudo add-apt-repository $apt_option ppa:ondrej/php || _install_status 1 "Unable to add-apt-repository ppa:ondrej/php" diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index c7bbac5c0..db5154e5c 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -62,9 +62,4 @@ 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 -www-data ALL=(ALL) NOPASSWD:/tmp/iptables_raspap.sh -www-data ALL=(ALL) NOPASSWD:/tmp/ip6tables_raspap.sh -www-data ALL=(ALL) NOPASSWD:/usr/sbin/iptables-save -www-data ALL=(ALL) NOPASSWD:/usr/sbin/ip6tables-save -www-data ALL=(ALL) NOPASSWD:/usr/bin/tee /etc/iptables/rules.v4 -www-data ALL=(ALL) NOPASSWD:/usr/bin/tee /etc/iptables/rules.v6 + diff --git a/templates/dashboard.php b/templates/dashboard.php index 113d004d5..597dc6b5d 100755 --- a/templates/dashboard.php +++ b/templates/dashboard.php @@ -3,17 +3,18 @@
-
- -
-
- -
+
+ +
+
+ +
+
@@ -32,97 +33,37 @@
-

+

- - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
'.$gw.""; ?>
-
- -
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
- +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
- -
- - -
- -
-
-
@@ -152,7 +93,7 @@ - + @@ -176,41 +117,28 @@
+
- + - - " name="ifup_wlan0" data-toggle="modal" data-target="#switchClientModal"/> - - " name="ifdown_wlan0" data-toggle="modal" data-target="#switchClientModal"/> + + " name="ifup_wlan0" /> + + " name="ifdown_wlan0" /> - +
+
- - -