From 0bac7decccf214e9b85e522f7dfcd5f682259748 Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 14 Mar 2025 03:35:32 -0700 Subject: [PATCH 1/7] Define helperScriptPath, create installRepositoryKeys() --- src/RaspAP/Plugins/PluginInstaller.php | 36 +++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/src/RaspAP/Plugins/PluginInstaller.php b/src/RaspAP/Plugins/PluginInstaller.php index 57846b05..bbc2c507 100644 --- a/src/RaspAP/Plugins/PluginInstaller.php +++ b/src/RaspAP/Plugins/PluginInstaller.php @@ -23,6 +23,7 @@ class PluginInstaller private $rootPath; private $pluginsManifest; private $repoPublic; + private $helperScriptPath; public function __construct() { @@ -34,6 +35,7 @@ class PluginInstaller $this->rootPath = $_SERVER['DOCUMENT_ROOT']; $this->pluginsManifest = '/plugins/manifest.json'; $this->repoPublic = $this->getRepository(); + $this->helperScriptPath = RASPI_CONFIG.'/plugins/plugin_helper.sh'; } // Returns a single instance of PluginInstaller @@ -187,6 +189,9 @@ class PluginInstaller $this->addSudoers($manifest['sudoers']); $rollbackStack[] = 'removeSudoers'; } + if (!empty($manifest['keys'])) { + $this->installRepositoryKeys($manifest['keys']); + } if (!empty($manifest['dependencies'])) { $this->installDependencies($manifest['dependencies']); $rollbackStack[] = 'uninstallDependencies'; @@ -243,7 +248,7 @@ class PluginInstaller $cmd = sprintf('sudo visudo -cf %s', escapeshellarg($tmpSudoers)); $return = shell_exec($cmd); if (strpos(strtolower($return), 'parsed ok') !== false) { - $cmd = sprintf('sudo /etc/raspap/plugins/plugin_helper.sh sudoers %s', escapeshellarg($tmpSudoers)); + $cmd = sprintf('sudo %s sudoers %s', escapeshellarg($this->helperScriptPath), escapeshellarg($tmpSudoers)); $return = shell_exec($cmd); if (strpos(strtolower($return), 'ok') === false) { throw new \Exception('Plugin helper failed to install sudoers.'); @@ -263,7 +268,7 @@ class PluginInstaller $packages = array_keys($dependencies); $packageList = implode(' ', $packages); - $cmd = sprintf('sudo /etc/raspap/plugins/plugin_helper.sh packages %s', escapeshellarg($packageList)); + $cmd = sprintf('sudo %s packages %s', escapeshellarg($this->helperScriptPath), escapeshellarg($packageList)); $return = shell_exec($cmd); if (strpos(strtolower($return), 'ok') === false) { throw new \Exception('Plugin helper failed to install depedencies.'); @@ -283,7 +288,7 @@ class PluginInstaller $username = escapeshellarg($user['name']); $password = escapeshellarg($user['pass']); - $cmd = sprintf('sudo /etc/raspap/plugins/plugin_helper.sh user %s %s', $username, $password); + $cmd = sprintf('sudo %s user %s %s', escapeshellarg($this->helperScriptPath), $username, $password); $return = shell_exec($cmd); if (strpos(strtolower($return), 'ok') === false) { throw new \Exception('Plugin helper failed to create user: ' . $user['name']); @@ -306,7 +311,7 @@ class PluginInstaller $destination = $this->rootPath . '/' . ltrim($destination, '/'); } $destination = escapeshellarg($destination); - $cmd = sprintf('sudo /etc/raspap/plugins/plugin_helper.sh config %s %s', $source, $destination); + $cmd = sprintf('sudo %s config %s %s', escapeshellarg($this->helperScriptPath), $source, $destination); $return = shell_exec($cmd); if (strpos(strtolower($return), 'ok') === false) { throw new \Exception("Failed to copy configuration file: $source to $destination"); @@ -325,7 +330,7 @@ class PluginInstaller foreach ($javascript as $js) { $source = escapeshellarg($pluginDir . DIRECTORY_SEPARATOR . $js); $destination = escapeshellarg($this->rootPath . DIRECTORY_SEPARATOR . 'app/js/plugins/'); - $cmd = sprintf('sudo /etc/raspap/plugins/plugin_helper.sh javascript %s %s', $source, $destination); + $cmd = sprintf('sudo %s javascript %s %s', escapeshellarg($this->helperScriptPath), $source, $destination); $return = shell_exec($cmd); if (strpos(strtolower($return), 'ok') === false) { throw new \Exception("Failed to copy JavaScript file: $source"); @@ -343,13 +348,32 @@ class PluginInstaller { $source = escapeshellarg($source); $destination = escapeshellarg($destination . DIRECTORY_SEPARATOR .$this->pluginPath . DIRECTORY_SEPARATOR . $this->pluginName); - $cmd = sprintf('sudo /etc/raspap/plugins/plugin_helper.sh plugin %s %s', $source, $destination); + $cmd = sprintf('sudo %s plugin %s %s', escapeshellarg($this->helperScriptPath), $source, $destination); $return = shell_exec($cmd); if (strpos(strtolower($return), 'ok') === false) { throw new \Exception('Failed to copy plugin files to: ' . $destination); } } + /** + * Install repository keys for third-party apt packages + * + * @param array $keys Array of repository URLs and their associated key URLs + * @throws Exception If the key installation fails. + */ + public function installRepositoryKeys(array $keys) + { + foreach ($keys as $repo => $keyUrl) { + $repoUrl = escapeshellarg($repo); + $keyUrl = escapeshellarg($keyUrl); + $cmd = sprintf('sudo %s keys %s %s', escapeshellarg($this->helperScriptPath), $repoUrl, $keyUrl); + $return = shell_exec($cmd); + if (strpos(strtolower($return), 'ok') === false) { + throw new \Exception("Failed to add repository and key for $repo"); + } + } + } + /** * Parses and returns a downloaded plugin manifest * From c7538653058fbb573e2bddc27c5f5975777e44a7 Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 14 Mar 2025 03:36:43 -0700 Subject: [PATCH 2/7] Added handling for GPG keys to enable third-party apt repos --- installers/plugin_helper.sh | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/installers/plugin_helper.sh b/installers/plugin_helper.sh index ecf4c26a..7e62b619 100755 --- a/installers/plugin_helper.sh +++ b/installers/plugin_helper.sh @@ -122,6 +122,34 @@ case "$action" in echo "OK" ;; + "keys") + [ $# -ne 2 ] && { echo "Usage: $0 keys "; exit 1; } + + repo="$1" + keyUrl="$2" + + keyringDir="/etc/apt/keyrings" + keyringPath="$keyringDir/$(basename "$keyUrl")" + + # ensure the keyring directory exists + sudo mkdir -p "$keyringDir" + + # download key and save it to the keyring + echo "Downloading GPG key for $repo from $keyUrl..." + sudo curl -fsSL "$keyUrl" -o "$keyringPath" || { echo "Failed to download GPG key for $repo"; exit 1; } + + # add the repository to the sources list with signed-by option + repoListFile="/etc/apt/sources.list.d/$(basename "$repo").list" + echo "deb [signed-by=$keyringPath] $repo" | sudo tee "$repoListFile" > /dev/null || { echo "Failed to add repository for $repo"; exit 1; } + + echo "Successfully added $repo with key from $keyUrl" + + # update apt package list + sudo apt-get update || { echo "Error: Failed to update apt"; exit 1; } + + echo "OK" + ;; + *) echo "Invalid action: $action" echo "Usage: $0 [parameters...]" @@ -131,7 +159,8 @@ case "$action" in echo " user Add user non-interactively" echo " config Applies a config file" echo " javascript Applies a JavaScript file" - echo " plugin Copies a plugin directory" + echo " plugin Copies a plugin directory" + echo " keys Installs a GPG key for a third-party repo" exit 1 ;; esac From bb131a7f5315d708cc1954723677bad4e607fbfa Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 14 Mar 2025 11:35:30 -0700 Subject: [PATCH 3/7] Revise key handling with parameters for url, keyring, repo + list --- installers/plugin_helper.sh | 58 ++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/installers/plugin_helper.sh b/installers/plugin_helper.sh index 7e62b619..086220f2 100755 --- a/installers/plugin_helper.sh +++ b/installers/plugin_helper.sh @@ -123,30 +123,36 @@ case "$action" in ;; "keys") - [ $# -ne 2 ] && { echo "Usage: $0 keys "; exit 1; } + [ $# -ne 4 ] && { echo "Usage: $0 keys "; exit 1; } - repo="$1" - keyUrl="$2" + key_url="$1" + keyring="$2" + repo="$3" + list_file="$4" - keyringDir="/etc/apt/keyrings" - keyringPath="$keyringDir/$(basename "$keyUrl")" + # add repository GPG key if it doesn't already exist + if [ ! -f "$keyring" ]; then + echo "Downloading GPG key from $key_url..." + curl -fsSL "$key_url" | sudo tee "$keyring" > /dev/null || { echo "Error: Failed to download GPG key."; exit 1; } + else + echo "Repository GPG key already exists at $keyring" + fi - # ensure the keyring directory exists - sudo mkdir -p "$keyringDir" + # add repository list if not present + if [ ! -f "$list_file" ]; then + echo "Adding repository $repo to sources list" + curl -fsSL "$repo" | sudo tee "$list_file" > /dev/null || { echo "Error: Failed to add repository to sources list."; exit 1; } + update_required=1 + else + echo "Repository already exists in sources list" + fi - # download key and save it to the keyring - echo "Downloading GPG key for $repo from $keyUrl..." - sudo curl -fsSL "$keyUrl" -o "$keyringPath" || { echo "Failed to download GPG key for $repo"; exit 1; } - - # add the repository to the sources list with signed-by option - repoListFile="/etc/apt/sources.list.d/$(basename "$repo").list" - echo "deb [signed-by=$keyringPath] $repo" | sudo tee "$repoListFile" > /dev/null || { echo "Failed to add repository for $repo"; exit 1; } - - echo "Successfully added $repo with key from $keyUrl" - - # update apt package list - sudo apt-get update || { echo "Error: Failed to update apt"; exit 1; } + # update apt package list if required + if [ "$update_required" == "1" ]; then + sudo apt-get update || { echo "Error: Failed to update apt"; exit 1; } + fi + echo "Successfully added $repo with GPG key from $key_url to sources" echo "OK" ;; @@ -154,13 +160,13 @@ case "$action" in echo "Invalid action: $action" echo "Usage: $0 [parameters...]" echo "Actions:" - echo " sudoers Install a sudoers file" - echo " packages Install aptitude package(s)" - echo " user Add user non-interactively" - echo " config Applies a config file" - echo " javascript Applies a JavaScript file" - echo " plugin Copies a plugin directory" - echo " keys Installs a GPG key for a third-party repo" + echo " sudoers Install a sudoers file" + echo " packages Install aptitude package(s)" + echo " user Add user non-interactively" + echo " config Applies a config file" + echo " javascript Applies a JavaScript file" + echo " plugin Copies a plugin directory" + echo " keys Installs a GPG key for a third-party repo" exit 1 ;; esac From c2be25271b3a19400d2d97d7ce3b386f23068f71 Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 14 Mar 2025 11:36:17 -0700 Subject: [PATCH 4/7] Update w/ signed packages from manifest --- app/js/custom.js | 1 + templates/system.php | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/app/js/custom.js b/app/js/custom.js index 22f5a1dc..e65598ca 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -496,6 +496,7 @@ $('#install-user-plugin').on('shown.bs.modal', function (e) { $('#plugin-license').text(manifestData.license || 'Unknown'); $('#plugin-locale').text(manifestData.default_locale || 'Unknown'); $('#plugin-configuration').html(formatProperty(manifestData.configuration || 'None')); + $('#plugin-packages').html(formatProperty(manifestData.keys || 'None')); $('#plugin-dependencies').html(formatProperty(manifestData.dependencies || 'None')); $('#plugin-javascript').html(formatProperty(manifestData.javascript || 'None')); $('#plugin-sudoers').html(formatProperty(manifestData.sudoers || 'None')); diff --git a/templates/system.php b/templates/system.php index 84d2bcff..039614c4 100755 --- a/templates/system.php +++ b/templates/system.php @@ -151,6 +151,10 @@ + + + + From 3e0f1f16c14809dc20ddb176dc95eaa51a62df03 Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 14 Mar 2025 11:37:47 -0700 Subject: [PATCH 5/7] Revise installRepositoryKeys() w/ array of key data --- src/RaspAP/Plugins/PluginInstaller.php | 28 +++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/RaspAP/Plugins/PluginInstaller.php b/src/RaspAP/Plugins/PluginInstaller.php index bbc2c507..a21e08c4 100644 --- a/src/RaspAP/Plugins/PluginInstaller.php +++ b/src/RaspAP/Plugins/PluginInstaller.php @@ -191,6 +191,7 @@ class PluginInstaller } if (!empty($manifest['keys'])) { $this->installRepositoryKeys($manifest['keys']); + $rollbackStack[] = 'uninstallRepositoryKeys'; } if (!empty($manifest['dependencies'])) { $this->installDependencies($manifest['dependencies']); @@ -212,7 +213,6 @@ class PluginInstaller $this->copyPluginFiles($pluginDir, $this->rootPath); $rollbackStack[] = 'removePluginFiles'; } - return true; } catch (\Exception $e) { throw new \Exception('Installation step failed: ' . $e->getMessage()); @@ -356,20 +356,30 @@ class PluginInstaller } /** - * Install repository keys for third-party apt packages + * Installs repository keys for third-party apt packages * - * @param array $keys Array of repository URLs and their associated key URLs - * @throws Exception If the key installation fails. + * @param array $keys Array containing key_url, keyring, repo, and sources + * @throws Exception on key installation failure */ public function installRepositoryKeys(array $keys) { - foreach ($keys as $repo => $keyUrl) { - $repoUrl = escapeshellarg($repo); - $keyUrl = escapeshellarg($keyUrl); - $cmd = sprintf('sudo %s keys %s %s', escapeshellarg($this->helperScriptPath), $repoUrl, $keyUrl); + error_log("executing installRepositoryKeys()"); + + foreach ($keys as $keyData) { + if (!isset($keyData['key_url'], $keyData['keyring'], $keyData['repo'], $keyData['sources'])) { + throw new \Exception("Invalid repository key structure"); + } + $cmd = sprintf( + 'sudo %s keys %s %s %s %s', + escapeshellarg($this->helperScriptPath), + escapeshellarg($keyData['key_url']), + escapeshellarg($keyData['keyring']), + escapeshellarg($keyData['repo']), + escapeshellarg($keyData['sources']) + ); $return = shell_exec($cmd); if (strpos(strtolower($return), 'ok') === false) { - throw new \Exception("Failed to add repository and key for $repo"); + throw new \Exception("Failed to add repository and key for {$keyData['repo']}"); } } } From c3a210907a41631a5649e4774e7a6c31e840d055 Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 15 Mar 2025 00:58:53 -0700 Subject: [PATCH 6/7] Throw exception for invalid key structure, update messages --- src/RaspAP/Plugins/PluginInstaller.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/RaspAP/Plugins/PluginInstaller.php b/src/RaspAP/Plugins/PluginInstaller.php index a21e08c4..13b74a4d 100644 --- a/src/RaspAP/Plugins/PluginInstaller.php +++ b/src/RaspAP/Plugins/PluginInstaller.php @@ -361,13 +361,14 @@ class PluginInstaller * @param array $keys Array containing key_url, keyring, repo, and sources * @throws Exception on key installation failure */ - public function installRepositoryKeys(array $keys) + public function installRepositoryKeys(array $keys): void { - error_log("executing installRepositoryKeys()"); - + if (!is_array($keys)) { + throw new \Exception("Invalid repository key structure: array expected"); + } foreach ($keys as $keyData) { if (!isset($keyData['key_url'], $keyData['keyring'], $keyData['repo'], $keyData['sources'])) { - throw new \Exception("Invalid repository key structure"); + throw new \Exception("Invalid repository key structure: " . json_encode($keyData)); } $cmd = sprintf( 'sudo %s keys %s %s %s %s', @@ -379,7 +380,7 @@ class PluginInstaller ); $return = shell_exec($cmd); if (strpos(strtolower($return), 'ok') === false) { - throw new \Exception("Failed to add repository and key for {$keyData['repo']}"); + throw new \Exception("Failed to add repository and key"); } } } From 0331eb7b25668c9c1160aab7cf8631cda5494efd Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 15 Mar 2025 00:59:36 -0700 Subject: [PATCH 7/7] Minor: suppress debug output from key install --- installers/plugin_helper.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/installers/plugin_helper.sh b/installers/plugin_helper.sh index 086220f2..903111c9 100755 --- a/installers/plugin_helper.sh +++ b/installers/plugin_helper.sh @@ -152,7 +152,6 @@ case "$action" in sudo apt-get update || { echo "Error: Failed to update apt"; exit 1; } fi - echo "Successfully added $repo with GPG key from $key_url to sources" echo "OK" ;;