Merge branch 'feature/ssh-remote-connection-resource-configuration-7595'
resolves #7595
This commit is contained in:
commit
42de13a2b9
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Forms\Config\Resource;
|
||||
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Forms\Config\ResourceConfigForm;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Util\File;
|
||||
use Zend_Validate_Callback;
|
||||
|
||||
/**
|
||||
* Form class for adding/modifying ssh identity resources
|
||||
*/
|
||||
class SshResourceForm extends Form
|
||||
{
|
||||
/**
|
||||
* Initialize this form
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('form_config_resource_ssh');
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Form::createElements()
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
'text',
|
||||
'name',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => $this->translate('Resource Name'),
|
||||
'description' => $this->translate('The unique name of this resource')
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'text',
|
||||
'user',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => $this->translate('User'),
|
||||
'description' => $this->translate(
|
||||
'User to log in as on the remote Icinga instance. Please note that key-based SSH login must be'
|
||||
. ' possible for this user'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if ($this->getRequest()->getActionName() != 'editresource') {
|
||||
|
||||
$callbackValidator = new Zend_Validate_Callback(function ($value) {
|
||||
if (openssl_pkey_get_private($value) === false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
$callbackValidator->setMessage(
|
||||
$this->translate('The given SSH key is invalid'),
|
||||
Zend_Validate_Callback::INVALID_VALUE
|
||||
);
|
||||
|
||||
$this->addElement(
|
||||
'textarea',
|
||||
'private_key',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => $this->translate('Private Key'),
|
||||
'description' => $this->translate('The private key which will be used for the SSH connections'),
|
||||
'class' => 'resource ssh-identity',
|
||||
'validators' => array($callbackValidator)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$resourceName = $formData['name'];
|
||||
$this->addElement(
|
||||
'note',
|
||||
'private_key_note',
|
||||
array(
|
||||
'escape' => false,
|
||||
'label' => $this->translate('Private Key'),
|
||||
'value' => sprintf(
|
||||
'<a href="%1$s" data-base-target="_next" title="%2$s" aria-label="%2$s">%3$s</a>',
|
||||
$this->getView()->url('config/removeresource', array('resource' => $resourceName)),
|
||||
sprintf($this->translate(
|
||||
'Remove the %s resource'
|
||||
), $resourceName),
|
||||
$this->translate('To modify the private key you must recreate this resource.')
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the assigned key to the resource
|
||||
*
|
||||
* @param ConfigObject $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function beforeRemove(ConfigObject $config)
|
||||
{
|
||||
$file = $config->private_key;
|
||||
|
||||
if (file_exists($file)) {
|
||||
unlink($file);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the assigned key to the resource
|
||||
*
|
||||
* @param ResourceConfigForm $form
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function beforeAdd(ResourceConfigForm $form)
|
||||
{
|
||||
$configDir = Icinga::app()->getConfigDir();
|
||||
$user = $form->getElement('user')->getValue();
|
||||
|
||||
$filePath = $configDir . '/ssh/' . $user;
|
||||
|
||||
if (! file_exists($filePath)) {
|
||||
$file = File::create($filePath, 0600);
|
||||
} else {
|
||||
$form->error(
|
||||
sprintf($form->translate('The private key for the user "%s" is already exists.'), $user)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
$file->fwrite($form->getElement('private_key')->getValue());
|
||||
|
||||
$form->getElement('private_key')->setValue($configDir . '/ssh/' . $user);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ use Icinga\Forms\Config\Resource\DbResourceForm;
|
|||
use Icinga\Forms\Config\Resource\FileResourceForm;
|
||||
use Icinga\Forms\Config\Resource\LdapResourceForm;
|
||||
use Icinga\Forms\Config\Resource\LivestatusResourceForm;
|
||||
use Icinga\Forms\Config\Resource\SshResourceForm;
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
|
||||
|
@ -41,6 +42,8 @@ class ResourceConfigForm extends ConfigForm
|
|||
return new LivestatusResourceForm();
|
||||
} elseif ($type === 'file') {
|
||||
return new FileResourceForm();
|
||||
} elseif ($type === 'ssh') {
|
||||
return new SshResourceForm();
|
||||
} else {
|
||||
throw new InvalidArgumentException(sprintf($this->translate('Invalid resource type "%s" provided'), $type));
|
||||
}
|
||||
|
@ -55,7 +58,7 @@ class ResourceConfigForm extends ConfigForm
|
|||
*
|
||||
* @return $this
|
||||
*
|
||||
* @thrwos InvalidArgumentException In case the resource does already exist
|
||||
* @throws InvalidArgumentException In case the resource does already exist
|
||||
*/
|
||||
public function add(array $values)
|
||||
{
|
||||
|
@ -116,6 +119,11 @@ class ResourceConfigForm extends ConfigForm
|
|||
}
|
||||
|
||||
$resourceConfig = $this->config->getSection($name);
|
||||
$resourceForm = $this->getResourceForm($resourceConfig->type);
|
||||
if (method_exists($resourceForm, 'beforeRemove')) {
|
||||
$resourceForm::beforeRemove($resourceConfig);
|
||||
}
|
||||
|
||||
$this->config->removeSection($name);
|
||||
return $resourceConfig;
|
||||
}
|
||||
|
@ -130,8 +138,9 @@ class ResourceConfigForm extends ConfigForm
|
|||
*/
|
||||
public function onSuccess()
|
||||
{
|
||||
$resourceForm = $this->getResourceForm($this->getElement('type')->getValue());
|
||||
|
||||
if (($el = $this->getElement('force_creation')) === null || false === $el->isChecked()) {
|
||||
$resourceForm = $this->getResourceForm($this->getElement('type')->getValue());
|
||||
if (method_exists($resourceForm, 'isValidResource') && false === $resourceForm::isValidResource($this)) {
|
||||
$this->addElement($this->getForceCreationCheckbox());
|
||||
return false;
|
||||
|
@ -141,6 +150,11 @@ class ResourceConfigForm extends ConfigForm
|
|||
$resource = $this->request->getQuery('resource');
|
||||
try {
|
||||
if ($resource === null) { // create new resource
|
||||
if (method_exists($resourceForm, 'beforeAdd')) {
|
||||
if (! $resourceForm::beforeAdd($this)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$this->add($this->getValues());
|
||||
$message = $this->translate('Resource "%s" has been successfully created');
|
||||
} else { // edit existing resource
|
||||
|
@ -212,6 +226,7 @@ class ResourceConfigForm extends ConfigForm
|
|||
$resourceTypes = array(
|
||||
'file' => $this->translate('File'),
|
||||
'livestatus' => 'Livestatus',
|
||||
'ssh' => $this->translate('SSH Identity'),
|
||||
);
|
||||
if ($resourceType === 'ldap' || Platform::extensionLoaded('ldap')) {
|
||||
$resourceTypes['ldap'] = 'LDAP';
|
||||
|
|
|
@ -8,7 +8,7 @@ different files, when the information about a data source changes.
|
|||
|
||||
Each section in **config/resources.ini** represents a data source with the section name being the identifier used to
|
||||
reference this specific data source. Depending on the data source type, the sections define different directives.
|
||||
The available data source types are *db*, *ldap* and *livestatus* which will described in detail in the following
|
||||
The available data source types are *db*, *ldap*, *ssh* and *livestatus* which will described in detail in the following
|
||||
paragraphs.
|
||||
|
||||
### <a id="resources-configuration-database"></a> Database
|
||||
|
@ -64,6 +64,26 @@ bind_dn = "cn=admin,ou=people,dc=icinga,dc=org"
|
|||
bind_pw = admin`
|
||||
````
|
||||
|
||||
### <a id="resources-configuration-ssh"></a> SSH
|
||||
|
||||
A SSH resource contains the information about the user and the private key location, which can be used for the key-based
|
||||
ssh authentication.
|
||||
|
||||
Directive | Description
|
||||
--------------------|------------
|
||||
**type** | `ssh`
|
||||
**user** | The username to use when connecting to the server.
|
||||
**private_key** | The path to the private key of the user.
|
||||
|
||||
**Example:**
|
||||
|
||||
````
|
||||
[ssh]
|
||||
type = "ssh"
|
||||
user = "ssh-user"
|
||||
private_key = "/etc/icingaweb2/ssh/ssh-user"
|
||||
````
|
||||
|
||||
### <a id="resources-configuration-livestatus"></a> Livestatus
|
||||
|
||||
A Livestatus resource represents the location of a Livestatus socket which is used for fetching monitoring data.
|
||||
|
|
|
@ -63,7 +63,7 @@ class File extends SplFileObject
|
|||
throw new NotWritableError(sprintf('Path "%s" is not writable', $dirPath));
|
||||
}
|
||||
|
||||
$file = new static($path, 'x');
|
||||
$file = new static($path, 'x+');
|
||||
|
||||
if (! @chmod($path, $accessMode)) {
|
||||
$error = error_get_last();
|
||||
|
|
|
@ -3,10 +3,19 @@
|
|||
|
||||
namespace Icinga\Module\Monitoring\Forms\Config\Instance;
|
||||
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
use Icinga\Web\Form;
|
||||
|
||||
class RemoteInstanceForm extends Form
|
||||
{
|
||||
/**
|
||||
* The available monitoring instance resources split by type
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $resources;
|
||||
|
||||
/**
|
||||
* (non-PHPDoc)
|
||||
* @see Form::init() For the method documentation.
|
||||
|
@ -16,12 +25,89 @@ class RemoteInstanceForm extends Form
|
|||
$this->setName('form_config_monitoring_instance_remote');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all available ssh identity resources
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws \Icinga\Exception\ConfigurationError
|
||||
*/
|
||||
public function loadResources()
|
||||
{
|
||||
$resourceConfig = ResourceFactory::getResourceConfigs();
|
||||
|
||||
$resources = array();
|
||||
foreach ($resourceConfig as $name => $resource) {
|
||||
if ($resource->type === 'ssh') {
|
||||
$resources['ssh'][$name] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($resources)) {
|
||||
throw new ConfigurationError($this->translate('Could not find any valid monitoring instance resources'));
|
||||
}
|
||||
|
||||
$this->resources = $resources;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPDoc)
|
||||
* @see Form::createElements() For the method documentation.
|
||||
*/
|
||||
public function createElements(array $formData = array())
|
||||
{
|
||||
$useResource = isset($formData['use_resource']) ? $formData['use_resource'] : $this->getValue('use_resource');
|
||||
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
'use_resource',
|
||||
array(
|
||||
'label' => $this->translate('Use SSH Identity'),
|
||||
'description' => $this->translate('Make use of the ssh identity resource'),
|
||||
'autosubmit' => true,
|
||||
'ignore' => true
|
||||
)
|
||||
);
|
||||
|
||||
if ($useResource) {
|
||||
|
||||
$this->loadResources();
|
||||
|
||||
$decorators = static::$defaultElementDecorators;
|
||||
array_pop($decorators); // Removes the HtmlTag decorator
|
||||
|
||||
$this->addElement(
|
||||
'select',
|
||||
'resource',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => $this->translate('SSH Identity'),
|
||||
'description' => $this->translate('The resource to use'),
|
||||
'decorators' => $decorators,
|
||||
'multiOptions' => $this->resources['ssh'],
|
||||
'value' => current($this->resources['ssh']),
|
||||
'autosubmit' => false
|
||||
)
|
||||
);
|
||||
$resourceName = isset($formData['resource']) ? $formData['resource'] : $this->getValue('resource');
|
||||
$this->addElement(
|
||||
'note',
|
||||
'resource_note',
|
||||
array(
|
||||
'escape' => false,
|
||||
'decorators' => $decorators,
|
||||
'value' => sprintf(
|
||||
'<a href="%1$s" data-base-target="_next" title="%2$s" aria-label="%2$s">%3$s</a>',
|
||||
$this->getView()->url('config/editresource', array('resource' => $resourceName)),
|
||||
sprintf($this->translate('Show the configuration of the %s resource'), $resourceName),
|
||||
$this->translate('Show resource configuration')
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->addElements(array(
|
||||
array(
|
||||
'text',
|
||||
|
@ -43,8 +129,11 @@ class RemoteInstanceForm extends Form
|
|||
'description' => $this->translate('SSH port to connect to on the remote Icinga instance'),
|
||||
'value' => 22
|
||||
)
|
||||
),
|
||||
array(
|
||||
)
|
||||
));
|
||||
|
||||
if (! $useResource) {
|
||||
$this->addElement(
|
||||
'text',
|
||||
'user',
|
||||
array(
|
||||
|
@ -55,18 +144,20 @@ class RemoteInstanceForm extends Form
|
|||
. ' possible for this user'
|
||||
)
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$this->addElement(
|
||||
'text',
|
||||
'path',
|
||||
array(
|
||||
'text',
|
||||
'path',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => $this->translate('Command File'),
|
||||
'value' => '/var/run/icinga2/cmd/icinga2.cmd',
|
||||
'description' => $this->translate('Path to the Icinga command file on the remote Icinga instance')
|
||||
)
|
||||
'required' => true,
|
||||
'label' => $this->translate('Command File'),
|
||||
'value' => '/var/run/icinga2/cmd/icinga2.cmd',
|
||||
'description' => $this->translate('Path to the Icinga command file on the remote Icinga instance')
|
||||
)
|
||||
));
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,6 +143,11 @@ class InstanceConfigForm extends ConfigForm
|
|||
|
||||
$instanceConfig = $this->config->getSection($instanceName)->toArray();
|
||||
$instanceConfig['name'] = $instanceName;
|
||||
|
||||
if (isset($instanceConfig['resource'])) {
|
||||
$instanceConfig['use_resource'] = true;
|
||||
}
|
||||
|
||||
$this->populate($instanceConfig);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
## Abstract
|
||||
|
||||
The instance.ini defines how icingaweb accesses the command pipe of your icinga process in order to submit external
|
||||
commands. When you are at the root of your icingaweb installation you can find it under ./config/modules/monitoring/instances.ini.
|
||||
commands. Depending on the config path (default: /etc/icingaweb2) of your icingaweb installation you can find it
|
||||
under ./modules/monitoring/instances.ini.
|
||||
|
||||
## Syntax
|
||||
|
||||
|
@ -33,5 +34,22 @@ setup key authentication at the endpoint and allow your icingweb's user to acces
|
|||
port=22 ; the port to use (22 if none is given)
|
||||
user=jdoe ; the user to authenticate with
|
||||
|
||||
You can also make use of the ssh resource for accessing an icinga pipe with key-based authentication, which will give
|
||||
you the possibility to define the location of the private key for a specific user, let's have a look:
|
||||
|
||||
[icinga]
|
||||
path=/usr/local/icinga/var/rw/icinga.cmd ; the path on the remote machine where the icinga.cmd can be found
|
||||
host=my.remote.machine.com ; the hostname or address of the remote machine
|
||||
port=22 ; the port to use (22 if none is given)
|
||||
resource=ssh ; the ssh resource which contains the username and the location of the private key
|
||||
|
||||
And the associated ssh resource:
|
||||
|
||||
[ssh]
|
||||
type = "ssh"
|
||||
user = "ssh-user"
|
||||
private_key = "/etc/icingaweb2/ssh/ssh-user"
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
namespace Icinga\Module\Monitoring\Command\Transport;
|
||||
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
use Icinga\Module\Monitoring\Command\Exception\TransportException;
|
||||
use Icinga\Module\Monitoring\Command\IcingaCommand;
|
||||
|
@ -44,6 +45,13 @@ class RemoteCommandFile implements CommandTransportInterface
|
|||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Path to the private key file for the key-based authentication
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $privateKey;
|
||||
|
||||
/**
|
||||
* Path to the Icinga command file on the remote host
|
||||
*
|
||||
|
@ -137,6 +145,55 @@ class RemoteCommandFile implements CommandTransportInterface
|
|||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the path to the private key file
|
||||
*
|
||||
* @param string $privateKey
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPrivateKey($privateKey)
|
||||
{
|
||||
$this->privateKey = (string) $privateKey;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the private key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrivateKey()
|
||||
{
|
||||
return $this->privateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a given resource to set the user and the key
|
||||
*
|
||||
* @param string
|
||||
*
|
||||
* @throws ConfigurationError
|
||||
*/
|
||||
public function setResource($resource = null)
|
||||
{
|
||||
$config = ResourceFactory::getResourceConfig($resource);
|
||||
|
||||
if (! isset($config->user)) {
|
||||
throw new ConfigurationError(
|
||||
t("Can't send external Icinga Command. Remote user is missing")
|
||||
);
|
||||
}
|
||||
if (! isset($config->private_key)) {
|
||||
throw new ConfigurationError(
|
||||
t("Can't send external Icinga Command. The private key for the remote user is missing")
|
||||
);
|
||||
}
|
||||
|
||||
$this->setUser($config->user);
|
||||
$this->setPrivateKey($config->private_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the path to the Icinga command file on the remote host
|
||||
*
|
||||
|
@ -192,6 +249,9 @@ class RemoteCommandFile implements CommandTransportInterface
|
|||
if (isset($this->user)) {
|
||||
$ssh .= sprintf(' -l %s', escapeshellarg($this->user));
|
||||
}
|
||||
if (isset($this->privateKey)) {
|
||||
$ssh .= sprintf(' -o StrictHostKeyChecking=no -i %s', escapeshellarg($this->privateKey));
|
||||
}
|
||||
$ssh .= sprintf(
|
||||
' %s "echo %s > %s" 2>&1', // Redirect stderr to stdout
|
||||
escapeshellarg($this->host),
|
||||
|
|
|
@ -210,6 +210,13 @@ textarea {
|
|||
height: 4em;
|
||||
}
|
||||
|
||||
textarea.resource {
|
||||
&.ssh-identity {
|
||||
width: 50%;
|
||||
height: 25em;
|
||||
}
|
||||
}
|
||||
|
||||
form .description {
|
||||
font-size: 0.8em;
|
||||
margin: 0.3em 0 0 0.6em;
|
||||
|
|
Loading…
Reference in New Issue