From 11c488215620d12c1f79fc9b05deb9928d2cab39 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Mon, 16 Nov 2020 14:03:21 +0100 Subject: [sysconfig] SSH: Split pubkey and rest of config, add more options Now we can have exactly one SSH-Config per sysconfig, which avoids confusion due to config mismatch regarding "allow pw" and "port". The install include takes care of splitting the key into a new module for existing modules, but doesn't remove duplicate SshConfig modules from sysconfigs, as this might lead to additional confusion. Next time the user edits a sysconfig, they are forced to pick exactly one SshConfig module. The "allow password login" option was extended to allow password login for non-root users only in addition to simply being "yes" or "no". There's an additional option that can entirely limit the group of users allowed to log in via SSH. --- .../sysconfig/inc/configmodule.inc.php | 10 ++-- .../sysconfig/inc/configmodule/sshconfig.inc.php | 32 +++++++++---- .../sysconfig/inc/configmodule/sshkey.inc.php | 55 ++++++++++++++++++++++ modules-available/sysconfig/inc/configtgz.inc.php | 17 ++++--- 4 files changed, 95 insertions(+), 19 deletions(-) create mode 100644 modules-available/sysconfig/inc/configmodule/sshkey.inc.php (limited to 'modules-available/sysconfig/inc') diff --git a/modules-available/sysconfig/inc/configmodule.inc.php b/modules-available/sysconfig/inc/configmodule.inc.php index a9035d78..f3906378 100644 --- a/modules-available/sysconfig/inc/configmodule.inc.php +++ b/modules-available/sysconfig/inc/configmodule.inc.php @@ -321,23 +321,25 @@ abstract class ConfigModule * * @return boolean true on success, false otherwise */ - public final function update($title) + public final function update($title = '') { if ($this->moduleId === 0) Util::traceError('ConfigModule::update called when moduleId == 0'); - if (empty($title)) - $title = $this->moduleTitle; + if (!empty($title)) { + $this->moduleTitle = $title; + } if (!$this->validateConfig()) return false; // Update Database::exec("UPDATE configtgz_module SET title = :title, contents = :contents, status = :status, dateline = :now " . " WHERE moduleid = :moduleid LIMIT 1", array( 'moduleid' => $this->moduleId, - 'title' => $title, + 'title' => $this->moduleTitle, 'contents' => json_encode($this->moduleData), 'status' => 'OUTDATED', 'now' => time(), )); + $this->moduleStatus = 'OUTDATED'; return true; } diff --git a/modules-available/sysconfig/inc/configmodule/sshconfig.inc.php b/modules-available/sysconfig/inc/configmodule/sshconfig.inc.php index 9975f789..b5ab20e4 100644 --- a/modules-available/sysconfig/inc/configmodule/sshconfig.inc.php +++ b/modules-available/sysconfig/inc/configmodule/sshconfig.inc.php @@ -5,7 +5,7 @@ ConfigModule::registerModule( Dictionary::translateFileModule('sysconfig', 'config-module', 'sshconfig_title'), // Title Dictionary::translateFileModule('sysconfig', 'config-module', 'sshconfig_description'), // Description Dictionary::translateFileModule('sysconfig', 'config-module', 'group_sshconfig'), // Group - false, // Only one per config? + true, // Only one per config? 500 ); @@ -23,7 +23,6 @@ class ConfigModule_SshConfig extends ConfigModule 'failOnParentFail' => false, 'parent' => $parent ); - // Create config module, which will also check if the pubkey is valid return Taskmanager::submit('SshdConfigGenerator', $config); } @@ -34,25 +33,40 @@ class ConfigModule_SshConfig extends ConfigModule protected function validateConfig() { - return isset($this->moduleData['publicKey']) && isset($this->moduleData['allowPasswordLogin']) && isset($this->moduleData['listenPort']); + // UPGRADE + if (isset($this->moduleData['allowPasswordLogin']) && !isset($this->moduleData['allowedUsersLogin'])) { + $this->moduleData['allowPasswordLogin'] = strtoupper($this->moduleData['allowPasswordLogin']); + if (!in_array($this->moduleData['allowPasswordLogin'], ['NO', 'USER_ONLY', 'YES'])) { + $this->moduleData['allowPasswordLogin'] = 'NO'; + } + $this->moduleData['allowedUsersLogin'] = 'ALL'; + } + return isset($this->moduleData['allowPasswordLogin']) && isset($this->moduleData['allowedUsersLogin']) + && isset($this->moduleData['listenPort']); } public function setData($key, $value) { switch ($key) { case 'publicKey': - break; + if ($value === false) { + error_log('Unsetting publicKey'); + unset($this->moduleData[$key]); + return true; + } + return false; case 'allowPasswordLogin': - if ($value === true || $value === 'yes') - $value = 'yes'; - elseif ($value === false || $value === 'no') - $value = 'no'; - else + if (!in_array($value, ['NO', 'USER_ONLY', 'YES'])) + return false; + break; + case 'allowedUsersLogin'; + if (!in_array($value, ['ROOT_ONLY', 'USER_ONLY', 'ALL'])) return false; break; case 'listenPort': if (!is_numeric($value) || $value < 1 || $value > 65535) return false; + $value = (int)$value; break; default: return false; diff --git a/modules-available/sysconfig/inc/configmodule/sshkey.inc.php b/modules-available/sysconfig/inc/configmodule/sshkey.inc.php new file mode 100644 index 00000000..2d212d25 --- /dev/null +++ b/modules-available/sysconfig/inc/configmodule/sshkey.inc.php @@ -0,0 +1,55 @@ +validateConfig()) + return false; + $config = array( + 'files' => [ + '/root/.ssh/authorized_keys.d/sshkey_' . $this->id() . '_' . Util::sanitizeFilename($this->title()) . '.pub' + => $this->moduleData['publicKey']], + 'destination' => $tgz, + 'failOnParentFail' => false, + 'parent' => $parent + ); + // Create config module, which will also check if the pubkey is valid + return Taskmanager::submit('MakeTarball', $config); + } + + protected function moduleVersion() + { + return self::VERSION; + } + + protected function validateConfig() + { + return isset($this->moduleData['publicKey']); + } + + public function setData($key, $value) + { + switch ($key) { + case 'publicKey': + break; + default: + return false; + } + $this->moduleData[$key] = $value; + return true; + } + +} diff --git a/modules-available/sysconfig/inc/configtgz.inc.php b/modules-available/sysconfig/inc/configtgz.inc.php index ff9e306d..98f29753 100644 --- a/modules-available/sysconfig/inc/configtgz.inc.php +++ b/modules-available/sysconfig/inc/configtgz.inc.php @@ -56,7 +56,9 @@ class ConfigTgz { if (!is_array($moduleIds)) return false; - $this->configTitle = $title; + if (!empty($title)) { + $this->configTitle = $title; + } $this->modules = array(); // Get all modules to put in config $idstr = '0'; // Passed directly in query. Make sure no SQL injection is possible @@ -77,7 +79,7 @@ class ConfigTgz // Update name Database::exec("UPDATE configtgz SET title = :title, status = :status, dateline = :now WHERE configid = :configid LIMIT 1", array( 'configid' => $this->configId, - 'title' => $title, + 'title' => $this->configTitle, 'status' => 'OUTDATED', 'now' => time(), )); @@ -88,9 +90,10 @@ class ConfigTgz * * @param bool $deleteOnError * @param int $timeoutMs + * @param string|null $parentTask parent task to order this rebuild after * @return string|bool true=success, false=error, string=taskid, still running */ - public function generate($deleteOnError = false, $timeoutMs = 0) + public function generate($deleteOnError = false, $timeoutMs = 0, $parentTask = null) { if (!($this->configId > 0) || !is_array($this->modules) || $this->file === false) Util::traceError ('configId <= 0 or modules not array in ConfigTgz::rebuild()'); @@ -103,7 +106,7 @@ class ConfigTgz } } - $task = self::recompress($files, $this->file); + $task = self::recompress($files, $this->file, $parentTask); // Wait for completion if ($timeoutMs > 0 && !Taskmanager::isFailed($task) && !Taskmanager::isFinished($task)) { @@ -215,7 +218,7 @@ class ConfigTgz * @param string $destFile where to store final result * @return false|array taskmanager task */ - private static function recompress($files, $destFile) + private static function recompress($files, $destFile, $parentTask = null) { // Get stuff other modules want to inject $handler = function($hook) { @@ -232,7 +235,9 @@ class ConfigTgz // Hand over to tm return Taskmanager::submit('RecompressArchive', array( 'inputFiles' => $files, - 'outputFile' =>$destFile + 'outputFile' =>$destFile, + 'parentTask' => $parentTask, + 'failOnParentFail' => false, )); } -- cgit v1.2.3-55-g7522