Merge branch 'master' into feature/monitoring-restrictions-9009

Conflicts:
	modules/monitoring/application/controllers/ShowController.php
	modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php
This commit is contained in:
Johannes Meyer 2015-06-16 17:12:51 +02:00
commit e8058c09c5
41 changed files with 1157 additions and 227 deletions

View File

@ -18,6 +18,7 @@ Marius Hein <marius.hein@netways.de>
Markus Frosch <markus@lazyfrosch.de>
Matthias Jentsch <matthias.jentsch@netways.de>
Michael Friedrich <michael.friedrich@netways.de>
Paul Richards <paul@minimoo.org>
Rene Moser <rene.moser@swisstxt.ch>
Susanne Vestner-Ludwig <susanne.vestner-ludwig@inserteffect.com>
Sylph Lin <sylph.lin@gmail.com>

View File

@ -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;
}
}

View File

@ -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';

View File

@ -31,15 +31,16 @@ if ($this->hasPermission('config/authentication/groups/edit') && $backend instan
<p class="group-name"><strong><?= $this->escape($group->group_name); ?></strong></p> <?= $editLink; ?>
<p class="group-created"><strong><?= $this->translate('Created at'); ?>:</strong> <?= $group->created_at === null ? '-' : $this->formatDateTime($group->created_at); ?></p>
<p class="group-modified"><strong><?= $this->translate('Last modified'); ?>:</strong> <?= $group->last_modified === null ? '-' : $this->formatDateTime($group->last_modified); ?></p>
<h3><?= $this->translate('Members'); ?></h3>
<?php if (! $this->compact): ?>
<?= $this->sortBox; ?>
<?php endif ?>
<?= $this->limiter; ?>
<?= $this->paginator; ?>
<?php if (! $this->compact): ?>
<?= $this->filterEditor; ?>
<?php endif ?>
</div>
<?php if (! $this->compact): ?>
<?= $this->sortBox; ?>
<?php endif ?>
<?= $this->limiter; ?>
<?= $this->paginator; ?>
<?php if (! $this->compact): ?>
<?= $this->filterEditor; ?>
<?php endif ?>
</div>
<div class="content members" data-base-target="_next">
<?php if (count($members) > 0): ?>

View File

@ -31,15 +31,16 @@ if ($this->hasPermission('config/authentication/users/edit') && $backend instanc
<p class="user-state"><strong><?= $this->translate('State'); ?>:</strong> <?= $user->is_active === null ? '-' : ($user->is_active ? $this->translate('Active') : $this->translate('Inactive')); ?></p>
<p class="user-created"><strong><?= $this->translate('Created at'); ?>:</strong> <?= $user->created_at === null ? '-' : $this->formatDateTime($user->created_at); ?></p>
<p class="user-modified"><strong><?= $this->translate('Last modified'); ?>:</strong> <?= $user->last_modified === null ? '-' : $this->formatDateTime($user->last_modified); ?></p>
<h3><?= $this->translate('Group Memberships'); ?></h3>
<?php if (! $this->compact): ?>
<?= $this->sortBox; ?>
<?php endif ?>
<?= $this->limiter; ?>
<?= $this->paginator; ?>
<?php if (! $this->compact): ?>
<?= $this->filterEditor; ?>
<?php endif ?>
</div>
<?php if (! $this->compact): ?>
<?= $this->sortBox; ?>
<?php endif ?>
<?= $this->limiter; ?>
<?= $this->paginator; ?>
<?php if (! $this->compact): ?>
<?= $this->filterEditor; ?>
<?php endif ?>
</div>
<div class="content memberships" data-base-target="_next">
<?php if (count($memberships) > 0): ?>

70
doc/about.md Normal file
View File

@ -0,0 +1,70 @@
# <a id="about"></a> About Icinga Web 2
Icinga Web 2 is a powerful PHP framework for web applications that comes in a clean and reduced design.
It's fast, responsive, accessible and easily extensible with modules.
## <a id="about-monitoring"></a> The monitoring module
This is the core module for most Icinga Web 2 users.
It provides an intuitive user interface for monitoring with Icinga (1 and 2).
Especially there are lots of list and detail views (e.g. for hosts and services)
you can sort and filter depending on what you want to see.
You can also control the monitoring process itself by sending external commands to Icinga.
Most such actions (like rescheduling a check) can be done with just a single click.
## <a id="about-installation"></a> Installation
Icinga Web 2 can be installed easily from packages from the official package repositories.
Setting it up is also easy with the web based setup wizard.
See [here](installation#installation) for more information about the installation.
## <a id="about-configuration"></a> Configuration
Icinga Web 2 can be configured via the user interface and .ini files.
See [here](configuration#configuration) for more information about the configuration.
## <a id="about-authentication"></a> Authentication
With Icinga Web 2 you can authenticate against relational databases, LDAP and more.
These authentication methods can be easily configured (via the corresponding .ini file).
See [here](authentication#authentication) for more information about
the different authentication methods available and how to configure them.
## <a id="about-authorization"></a> Authorization
In Icinga Web 2 there are permissions and restrictions to allow and deny (respectively)
roles to view or to do certain things.
These roles can be assigned to users and groups.
See [here](security#security) for more information about authorization
and how to configure roles.
## <a id="about-preferences"></a> User preferences
Besides the global configuration each user has individual configuration options
like the interface's language or the current timezone.
They can be stored either in a database or in .ini files.
See [here](preferences#preferences) for more information about a user's preferences
and how to configure their storage type.
## <a id="about-documentation"></a> Documentation
With the documentation module you can read the documentation of the framework (and any module) directly in the user interface.
The module can also export the documentation to PDF.
## <a id="about-translation"></a> Translation
With the translation module every piece of text in the user interface (of the framework itself and any module) can be translated to a language of your choice.
Currently provided languages:
* German
* Italian
* Portuguese

View File

@ -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.

279
doc/security.md Normal file
View File

@ -0,0 +1,279 @@
# <a id="security"></a> Security
Access control is a vital part of configuring Icinga Web 2 in a secure way.
It is important that not every user that has access to Icinga Web 2 is able
to do any action or to see any host and service. For example, it is useful to allow
only a small group of administrators to change the Icinga Web 2 configuration,
to prevent misconfiguration or security breaches. Another important use case is
creating groups of users which can only see the fraction of the monitoring
environment they are in charge of.
This chapter will describe how to do the security configuration of Icinga Web 2
and how to apply permissions and restrictions to users or groups of users.
## Basics
Icinga Web 2 access control is done by defining **roles** that associate permissions
and restrictions with **users** and **groups**. There are two general kinds of
things to which access can be managed: actions and objects.
### Actions
Actions are all the things an Icinga Web 2 user can do, like changing a certain configuration,
changing permissions or sending a command to the Icinga instance through the
<a href="http://docs.icinga.org/icinga2/latest/doc/module/icinga2/toc#!/icinga2/latest/doc/module/icinga2/chapter/getting-started#setting-up-external-command-pipe">Command Pipe</a>
in the monitoring module. All actions must be be **allowed explicitly** using permissions.
A permission is a simple list of identifiers of actions a user is
allowed to do. Permissions are described in greater detail in the
section [Permissions](#permissions).
### Objects
There are all kinds of different objects in Icinga Web 2: Hosts, Services, Notifications, Downtimes and Events.
By default, a user can **see everything**, but it is possible to **explicitly restrict** what each user can see using restrictions.
Restrictions are complex filter queries that describe what objects should be displayed to a user. Restrictions are described
in greater detail in the section [Restrictions](#restrictions).
### Users
Anyone who can **login** to Icinga Web 2 is considered a user and can be referenced to by the
**user name** used during login.
For example, there might be user called **jdoe** authenticated
using Active Directory, and a user **icingaadmin** that is authenticated using a MySQL-Database as backend.
In the configuration, both can be referenced to by using their user names **icingaadmin** or **jdoe**.
Icinga Web 2 users and groups are not configured by a configuration file, but provided by
an **authentication backend**. For extended information on setting up authentication backends and managing users, please read the chapter [Authentication](authentication.md#authentication).
<div class="info-box">
Since Icinga Web 2, users in the Icinga configuration and the web authentication are separated, to allow
use of external authentication providers. This means that users and groups defined in the Icinga configuration are not available to Icinga Web 2. Instead it uses its own authentication
backend to fetch users and groups from, which must be configured separately.
</div>
#### Managing Users
When using a [Database
as authentication backend](authentication.md#authentication-configuration-db-authentication), it is possible to create, add and delete users directly in the frontend. This configuration
can be found at **Configuration > Authentication > Users **.
### Groups
If there is a big amount of users to manage, it would be tedious to specify each user
separately when regularly referring to the same group of users. Because of that, it is possible to group users.
A user can be member of multiple groups and will inherit all permissions and restrictions.
Like users, groups are identified solely by their **name** that is provided by
a **group backend**. For extended information on setting up group backends,
please read the chapter [Authentication](authentication.md#authentication).
#### Managing Groups
When using a [Database as an authentication backend](#authentication.md#authentication-configuration-db-authentication),
it is possible to manage groups and group memberships directly in the frontend. This configuration
can be found at **Configuration > Authentication > Groups **.
## Roles
A role defines a set of **permissions** and **restrictions** and assigns
those to **users** and **groups**. For example, a role **admins** could define that certain
users have access to all configuration options, or another role **support**
could define that a list of users or groups is restricted to see only hosts and services
that match a specific query.
The actual permission of a certain user will be determined by merging the permissions
and restrictions of the user itself and all the groups the user is member of. Permissions can
be simply added up, while restrictions follow a slighty more complex pattern, that is described
in the section [Stacking Filters](#stacking-filters).
### Configuration
Roles can be changed either through the icingaweb2 interface, by navigation
to the page **Configuration > Authentication > Roles**, or through editing the
configuration file:
/etc/icingaweb2/roles.ini
#### Introducing Example
To get you a quick start, here is an example of what a role definition could look like:
[winadmin]
users = "jdoe, janedoe"
groups = "admin"
permissions = "config/application/*, monitoring/commands/schedule-check"
monitoring/filter/objects = "host=*win*"
This example creates a role called **winadmin**, that grants all permissions in `config/application/*` and `monitoring/commands/schedule-check` and additionally only
allows the hosts and services that match the filter `host=*win*` to be displayed. The users
**jdoe** and **janedoe** and all members of the group **admin** will be affected
by this role.
#### <a id="syntax"></a> Syntax
Each role is defined as a section, with the name of the role as section name. The following
attributes can be defined for each role in a default Icinga Web 2 installation:
Directive | Description
---------------------------|-----------------------------------------------------------------------------
users | A comma-separated list of user **user names** that are affected by this role
groups | A comma-separated list of **group names** that are affected by this role
permissions | A comma-separated list of **permissions** granted by this role
monitoring/filter/objects | A **filter expression** that restricts the access to services and hosts
## <a id="permissions"></a> Permissions
Permissions can be used to allow users or groups certain **actions**. By default,
all actions are **prohibited** and must be allowed explicitly by a role for any user.
Each action in Icinga Web 2 is denoted by a **namespaced key**, which is used to order and
group those actions. All actions that affect the configuration of Icinga Web 2, are in a
namespace called **config**, while all configurations that affect authentication
are in the namespace `config/authentication`
**Wildcards** can be used to grant permission for all actions in a certain namespace.
The permission `config/*` would grant permission to all configuration actions,
while just specifying a wildcard `*` would give permission for all actions.
When multiple roles assign permissions to the same user (either directly or indirectly
through a group) all permissions can simply be added together to get the users actual permission set.
#### Global permissions
Name | Permits
-------------------------------------|-----------------------------------------------------------------
* | Allow everything, including module-specific permissions
config/* | Allow all configuration actions
config/application/* | Allow configuring IcingaWeb2
config/application/general | Allow general settings, like logging or preferences
config/application/resources | Allow changing resources for retrieving data
config/application/userbackend | Allow changing backends for retrieving available users
config/application/usergroupbackend | Allow changing backends for retrieving available groups
config/authentication/* | Allow configuring IcingaWeb2 authentication mechanisms
config/authentication/users/* | Allow all user actions
config/authentication/users/show | Allow displaying avilable users
config/authentication/users/add | Allow adding a new user to the backend
config/authentication/users/edit | Allow editing an existing user in the backend
config/authentication/users/remove | Allow removing an existing user from the backend
config/authentication/groups/* | Allow all group actions
config/authentication/groups/show | Allow displaying all available groups
config/authentication/groups/add | Allow adding a new group to the backend
config/authentication/groups/edit | Allow editing existing groups in a backend
config/authentication/groups/remove | Allow removing existing groups from the backend
config/authentication/roles/* | Allow all role actions
config/authentication/roles/add | Allow adding a new role
config/authentication/roles/show | Allow displaying available roles
config/authentication/roles/edit | Allow changing an existing role
config/authentication/roles/remove | Allow removing an existing row
config/modules | Allow enabling or disabling modules
#### Monitoring module permissions
The built-in monitoring module defines an additional set of permissions, that
is described in detail in [monitoring module documentation](/icingaweb2/doc/module/doc/chapter/monitoring-security#monitoring-security).
## <a id="restrictions"></a> Restrictions
Restrictions can be used to define what a user or group can see by specifying
a filter expression that applies to a defined set of data. By default, when no
restrictions are defined, a user will be able to see every information that is available.
A restrictions is always specified for a certain **filter directive**, that defines what
data the filter is applied to. The **filter directive** is a simple identifier, that was
defined in an Icinga Web 2 module. The only filter directive that is available
in a default installation, is the `monitoring/filter/objects` directive, defined by the monitoring module,
that can be used to apply filter to hosts and services. This directive was previously
mentioned in the section [Syntax](#syntax).
### Filter Expressions
Filters operate on columns. A complete list of all available filter columns on hosts and services can be found in
the [monitoring module documentation](/icingaweb2/doc/module/doc/chapter/monitoring-security#monitoring-security-restrictions).
Any filter expression that is allowed in the filtered view, is also an allowed filter expression.
This means, that it is possible to define negations, wildcards, and even nested
filter expressions containing AND and OR-Clauses.
The filter expression will be **implicitly** added as an **AND-Clause** to each query on
the filtered data. The following shows the filter expression `host=*win*` being applied on `monitoring/filter/objects`.
Regular filter query:
AND-- service_problem = 1
|
+--- service_handled = 0
With our restriction applied, any user affected by this restrictions will see the
results of this query instead:
AND-- host = *win*
|
+--AND-- service_problem = 1
|
+--- service_handled = 0
#### <a id="stacking-filters"></a> Stacking Filters
When multiple roles assign restrictions to the same user, either directly or indirectly
through a group, all filters will be combined using an **OR-Clause**, resulting in the final
expression:
AND-- OR-- $FILTER1
| |
| +-- $FILTER2
| |
| +-- $FILTER3
|
+--AND-- service_problem = 1
|
+--- service_handled = 0
As a result, a user is be able to see hosts that are matched by **ANY** of
the filter expressions. The following examples will show the usefulness of this behavior:
#### Example 1: Negation
[winadmin]
groups = "windows-admins"
monitoring/filter/objects = "host=*win*"
Will display only hosts and services whose host name contains **win**.
[webadmin]
groups = "web-admins"
monitoring/filter/objects = "host!=*win*"
Will only match hosts and services whose host name does **not** contain **win**
Notice that because of the behavior of two stacking filters, a user that is member of **windows-admins** and **web-admins**, will now be able to see both, Windows and non-Windows hosts and services.
#### Example 2: Hostgroups
[unix-server]
groups = "unix-admins"
monitoring/filter/objects = "(hostgroup_name=bsd-servers|hostgroup_name=linux-servers)"
This role allows all members of the group unix-admins to see hosts and services
that are part of the host-group linux-servers or the host-group bsd-servers.

View File

@ -201,7 +201,7 @@ cp -prv packages/files/config/modules/setup %{buildroot}/%{configdir}/modules/
%pre
getent group icingacmd >/dev/null || groupadd -r icingacmd
%if 0%{?suse_version}
%if 0%{?suse_version} && 0%{?suse_version} < 01200
usermod -A icingacmd,%{icingawebgroup} %{wwwuser}
%else
usermod -a -G icingacmd,%{icingawebgroup} %{wwwuser}

View File

@ -134,18 +134,20 @@ abstract class ApplicationBootstrap
$this->vendorDir = $baseDir . '/library/vendor';
$this->libDir = realpath(__DIR__ . '/../..');
$this->setupAutoloader();
if ($configDir === null) {
if (array_key_exists('ICINGAWEB_CONFIGDIR', $_SERVER)) {
$configDir = $_SERVER['ICINGAWEB_CONFIGDIR'];
} else {
$configDir = '/etc/icingaweb2';
$configDir = Platform::isWindows()
? $baseDir . '/config'
: '/etc/icingaweb2';
}
}
$canonical = realpath($configDir);
$this->configDir = $canonical ? $canonical : $configDir;
$this->setupAutoloader();
set_include_path(
implode(
PATH_SEPARATOR,

View File

@ -31,7 +31,7 @@ $baseDir = $_SERVER['DOCUMENT_ROOT'];
$baseDir = dirname($_SERVER['SCRIPT_FILENAME']);
// Fix aliases
$remove = dirname($_SERVER['PHP_SELF']);
$remove = str_replace('\\', '/', dirname($_SERVER['PHP_SELF']));
if (substr($ruri, 0, strlen($remove)) !== $remove) {
return false;
}

View File

@ -47,7 +47,12 @@ class Params
$noOptionFlag = true;
} elseif (!$noOptionFlag && substr($argv[$i], 0, 2) === '--') {
$key = substr($argv[$i], 2);
if (! isset($argv[$i + 1]) || substr($argv[$i + 1], 0, 2) === '--') {
$matches = array();
if (1 === preg_match(
'/(?<!.)([^=]+)=(.*)(?!.)/ms', $key, $matches
)) {
$this->params[$matches[1]] = $matches[2];
} elseif (! isset($argv[$i + 1]) || substr($argv[$i + 1], 0, 2) === '--') {
$this->params[$key] = true;
} elseif (array_key_exists($key, $this->params)) {
if (!is_array($this->params[$key])) {

View File

@ -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();

View File

@ -100,7 +100,11 @@ class Translator
{
$contextString = "{$context}\004{$text}";
$translation = dcgettext($domain, $contextString, LC_MESSAGES);
$translation = dcgettext(
$domain,
$contextString,
defined('LC_MESSAGES') ? LC_MESSAGES : LC_ALL
);
if ($translation == $contextString) {
return $text;
@ -126,7 +130,13 @@ class Translator
{
$contextString = "{$context}\004{$textSingular}";
$translation = dcngettext($domain, $contextString, $textPlural, $number, LC_MESSAGES);
$translation = dcngettext(
$domain,
$contextString,
$textPlural,
$number,
defined('LC_MESSAGES') ? LC_MESSAGES : LC_ALL
);
if ($translation == $contextString || $translation == $textPlural) {
return ($number == 1 ? $textSingular : $textPlural);

View File

@ -237,6 +237,10 @@ class Menu implements RecursiveIterator
'priority' => 700,
'renderer' => 'ProblemMenuItemRenderer'
));
$section->add(t('About'), array(
'url' => 'about',
'priority' => 701
));
if (Logger::writesToFile()) {
$section->add(t('Application Log'), array(
'url' => 'list/applicationlog',
@ -279,11 +283,6 @@ class Menu implements RecursiveIterator
'priority' => 990,
'renderer' => 'ForeignMenuItemRenderer'
));
$this->add(t('About'), array(
'url' => 'about',
'priority' => 1000
));
}
}

View File

@ -374,9 +374,29 @@ class UrlParams
}
}
public function toArray()
/**
* Return the parameters of this url as sequenced or associative array
*
* @param bool $sequenced
*
* @return array
*/
public function toArray($sequenced = true)
{
return $this->params;
if ($sequenced) {
return $this->params;
}
$params = array();
foreach ($this->params as $param) {
if ($param[1] === true) {
$params[] = $param[0];
} else {
$params[$param[0]] = $param[1];
}
}
return $params;
}
public function toString($separator = null)

View File

@ -309,13 +309,14 @@ EOT;
private function renderRefreshTab()
{
$url = Url::fromRequest()->without('renderLayout');
$tab = $this->get($this->getActiveName());
if ($tab !== null) {
$url = Url::fromRequest($tab->getUrl()->getParams()->toArray(false))->without('renderLayout');
$label = $this->view()->escape(
$tab->getLabel()
);
} else {
$url = Url::fromRequest()->without('renderLayout');
}
if (! empty($label)) {

View File

@ -0,0 +1,65 @@
# <a id="monitoring-security"></a> Security
The monitoring module provides an additional set of restrictions and permissions
that can be used for access control. The following sections will list those
restrictions and permissions in detail:
## Permissions
The Icinga Web 2 monitoring module can send commands to the current Icinga2 instance
through the command pipe. A user needs specific permissions to be able to send those
commands when using the monitoring module.
| Name | Permits |
|---------------------------------------------|-----------------------------------------------------------------------------|
| monitoring/command/* | Allow all commands |
| monitoring/command/schedule-check | Allow scheduling host and service checks' |
| monitoring/command/acknowledge-problem | Allow acknowledging host and service problems |
| monitoring/command/remove-acknowledgement | Allow removing problem acknowledgements |
| monitoring/command/comment/* | Allow adding and deleting host and service comments |
| monitoring/command/comment/add | Allow commenting on hosts and services |
| monitoring/command/downtime/delete | Allow deleting host and service downtimes' |
| monitoring/command/process-check-result | Allow processing host and service check results |
| monitoring/command/feature/instance | Allow processing commands for toggling features on an instance-wide basis |
| monitoring/command/feature/object | Allow processing commands for toggling features on host and service objects |
| monitoring/command/send-custom-notification | Allow sending custom notifications for hosts and services |
## <a id="monitoring-security-restrictions"></a> Restrictions
The monitoring module allows filtering objects:
| Keys | Restricts |
|----------------------------|-----------------------------------------------|
| monitoring/filter/objects | Applies a filter to all hosts and services |
This filter will affect all hosts and services. Furthermore, it will also
affect all related objects, like notifications, downtimes or events. If a
service is hidden, all notifications, downtimes on that service will be hidden too.
### Filter Column Names
The following filter column names are available in filter expressions:
| Column |
|------------------------------------------------------|
| host |
| host_alias |
| host_display_name |
| host_name |
| hostgroup |
| hostgroup_alias |
| hostgroup_name |
| service |
| service_description |
| service_display_name |
| service_group |
| service_group_alias |
| service_group_name |
| + all custom variables prefixed with host or service |

View File

@ -1,4 +1,4 @@
Module: doc
Version: 2.0.0
Version: 2.0.0-rc1
Description: Documentation module
Extracts, shows and exports documentation for Icinga Web 2 and its modules.

View File

@ -20,7 +20,7 @@ class ConferenceCommand extends Command
* Use this command in case you feel that you should be friendly. Should
* be executed as follows:
*
* icingacli monitoring conference welcome --watch 1
* icingacli monitoring conference welcome --watch=1
*/
public function welcomeAction()
{

View File

@ -5,7 +5,7 @@ namespace Icinga\Module\Monitoring\Clicommands;
use Icinga\Module\Monitoring\Backend;
use Icinga\Module\Monitoring\Cli\CliUtils;
use Icinga\Util\Format;
use Icinga\Date\DateFormatter;
use Icinga\Cli\Command;
use Icinga\File\Csv;
use Icinga\Module\Monitoring\Plugin\PerfdataSet;
@ -124,19 +124,19 @@ class ListCommand extends Command
* --verbose Show detailled output
* --showsql Dump generated SQL query (DB backend only)
*
* --format <csv|json|<custom>>
* --format=<csv|json|<custom>>
* Dump columns in the given format. <custom> format allows $column$
* placeholders, e.g. --format '$host$: $service$'
* placeholders, e.g. --format='$host$: $service$'
*
* --<column> [filter]
* --<column>[=filter]
* Filter given column by optional filter. Boolean (1/0) columns are
* true if no filter value is given.
*
* EXAMPLES
*
* icingacli monitoring list --unhandled
* icingacli monitoring list --host local* --service *disk*
* icingacli monitoring list --format '$host$: $service$'
* icingacli monitoring list --host=local* --service=*disk*
* icingacli monitoring list --format='$host$: $service$'
*/
public function statusAction()
{
@ -299,7 +299,7 @@ class ListCommand extends Command
$leaf,
$screen->underline($row->service_description),
$screen->colorize($utils->objectStateFlags('service', $row) . $perf, 'lightblue'),
ucfirst(Format::timeSince($row->service_last_state_change))
ucfirst(DateFormatter::timeSince($row->service_last_state_change))
);
if ($this->isVerbose) {
$out .= $emptyLine . preg_replace(

View File

@ -27,8 +27,8 @@ class NrpeCommand extends Command
*
* EXAMPLE
*
* icingacli monitoring nrpe 127.0.0.1 CheckMEM --ssl --MaxWarn 80% \
* --MaxCrit 90% --type physical
* icingacli monitoring nrpe 127.0.0.1 CheckMEM --ssl --MaxWarn=80% \
* --MaxCrit=90% --type=physical
*/
public function checkAction()
{

View File

@ -63,6 +63,54 @@ class Monitoring_HostController extends MonitoredObjectController
parent::showAction();
}
/**
* List a host's services
*/
public function servicesAction()
{
$this->setAutorefreshInterval(10);
$this->getTabs()->activate('services');
$query = $this->backend->select()->from('servicestatus', array(
'host_name',
'host_display_name',
'host_state',
'host_state_type',
'host_last_state_change',
'host_address',
'host_handled',
'service_description',
'service_display_name',
'service_state',
'service_in_downtime',
'service_acknowledged',
'service_handled',
'service_output',
'service_perfdata',
'service_attempt',
'service_last_state_change',
'service_icon_image',
'service_icon_image_alt',
'service_is_flapping',
'service_state_type',
'service_handled',
'service_severity',
'service_last_check',
'service_notifications_enabled',
'service_action_url',
'service_notes_url',
'service_last_comment',
'service_last_ack',
'service_last_downtime',
'service_active_checks_enabled',
'service_passive_checks_enabled',
'current_check_attempt' => 'service_current_check_attempt',
'max_check_attempts' => 'service_max_check_attempts'
));
$this->applyRestriction('monitoring/filter/objects', $query);
$this->view->services = $query->where('host_name', $this->object->getName());
$this->view->object = $this->object;
}
/**
* Acknowledge a host problem
*/

View File

@ -1,12 +1,7 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Module\Monitoring\Object\MonitoredObject;
use Icinga\Web\Hook;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tabs;
use Icinga\Web\Widget\Tabextension\OutputFormat;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Module\Monitoring\Backend;
use Icinga\Module\Monitoring\Controller;
@ -22,34 +17,6 @@ class Monitoring_ShowController extends Controller
*/
protected $backend;
/**
* @var Hook\GrapherHook
*/
protected $grapher;
/**
* Initialize the controller
*/
public function init()
{
$this->view->object = MonitoredObject::fromParams($this->params);
if ($this->view->object && $this->view->object->fetch() === false) {
throw new Zend_Controller_Action_Exception($this->translate('Host or service not found'));
}
if (Hook::has('ticket')) {
$this->view->tickets = Hook::first('ticket');
}
if (Hook::has('grapher')) {
$this->grapher = Hook::first('grapher');
if ($this->grapher && ! $this->grapher->hasPreviews()) {
$this->grapher = null;
}
}
$this->createTabs();
}
/**
* @deprecated
*/
@ -66,36 +33,16 @@ class Monitoring_ShowController extends Controller
$this->redirectNow(Url::fromRequest()->setPath('monitoring/host/show'));
}
/**
* @deprecated
*/
public function historyAction()
{
$this->getTabs()->activate('history');
$this->view->object->fetchEventHistory();
$this->view->history = $this->view->object->eventhistory;
$this->handleFormatRequest($this->view->object->eventhistory);
$this->fetchHostStats();
if ($this->params->has('service')) {
$this->redirectNow(Url::fromRequest()->setPath('monitoring/service/history'));
}
$this->setupLimitControl(50);
$this->setupPaginationControl($this->view->history, 50);
}
protected function fetchHostStats()
{
$query = $this->backend->select()->from('servicestatussummary', array(
'services_total',
'services_ok',
'services_critical',
'services_critical_unhandled',
'services_critical_handled',
'services_warning',
'services_warning_unhandled',
'services_warning_handled',
'services_unknown',
'services_unknown_unhandled',
'services_unknown_handled',
'services_pending',
))->where('service_host_name', $this->params->get('host'));
$this->applyRestriction('monitoring/filter/objects', $query);
$this->view->stats = $query->getQuery()->fetchRow();
$this->redirectNow(Url::fromRequest()->setPath('monitoring/host/history'));
}
public function contactAction()
@ -163,78 +110,4 @@ class Monitoring_ShowController extends Controller
$this->view->contact = $contact;
$this->view->contactName = $contactName;
}
/**
* Creating tabs for this controller
* @return Tabs
*/
protected function createTabs()
{
if (($object = $this->view->object) === null) {
return;
}
if ($object->getType() === $object::TYPE_HOST) {
$isService = false;
$params = array(
'host' => $object->getName()
);
} else {
$isService = true;
$params = array(
'host' => $object->getHost()->getName(),
'service' => $object->getName()
);
}
$tabs = $this->getTabs();
$tabs->add(
'host',
array(
'title' => sprintf(
$this->translate('Show detailed information for host %s'),
$isService ? $object->getHost()->getName() : $object->getName()
),
'label' => $this->translate('Host'),
'icon' => 'host',
'url' => 'monitoring/show/host',
'urlParams' => $params,
)
);
if ($isService) {
$tabs->add(
'service',
array(
'title' => sprintf(
$this->translate('Show detailed information for service %s on host %s'),
$object->getName(),
$object->getHost()->getName()
),
'label' => $this->translate('Service'),
'icon' => 'service',
'url' => 'monitoring/show/service',
'urlParams' => $params,
)
);
}
if ($this->backend->hasQuery('eventhistory')) {
$tabs->add(
'history',
array(
'title' => $isService
? sprintf(
$this->translate('Show all event records of service %s on host %s'),
$object->getName(),
$object->getHost()->getName()
)
: sprintf($this->translate('Show all event records of host %s'), $object->getName())
,
'label' => $this->translate('History'),
'icon' => 'rewind',
'url' => 'monitoring/show/history',
'urlParams' => $params,
)
);
}
$tabs->extend(new OutputFormat())
->extend(new DashboardAction());
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -3,17 +3,12 @@ use Icinga\Module\Monitoring\Object\Host;
use Icinga\Module\Monitoring\Object\Service;
$self = $this;
$hostContext = $object->getType() === 'host';
if (! $this->compact): ?>
<div class="controls">
<?= $this->tabs; ?>
<?php if ($hostContext): ?>
<?= $this->render('partials/host/object-header.phtml'); ?>
<?php else: ?>
<?= $this->render('partials/service/object-header.phtml'); ?>
<?php endif ?>
<h1><?= $this->translate('This Object\'s Event History'); ?></h1>
<h1><?= $this->translate('This Host\'s Event History'); ?></h1>
<?= $this->sortBox; ?>
<?= $this->limiter; ?>
<?= $this->paginator; ?>
@ -24,7 +19,7 @@ if (! $this->compact): ?>
<?php
if (count($history) === 0) {
echo $this->translate('No history available for this object') . '</div>';
echo $this->translate('No history events found matching the filter') . '</div>';
return;
}
?>
@ -133,7 +128,7 @@ $output = $this->tickets ? preg_replace_callback(
<?php if ($isService): ?>
<?= sprintf(
$this->translate('%s on %s', 'Service running on host'),
$hostContext ? $this->qlink(
$this->qlink(
$event->service_display_name,
'monitoring/show/service',
array(
@ -145,7 +140,7 @@ $output = $this->tickets ? preg_replace_callback(
$event->service_display_name,
$event->host_display_name
))
) : $this->escape($event->service_display_name),
),
$event->host_display_name
) ?>
<?php else: ?>

View File

@ -0,0 +1,17 @@
<div class="controls">
<?php if (! $this->compact): ?>
<?= $this->tabs; ?>
<?php endif ?>
<?= $this->render('partials/host/object-header.phtml') ?>
<?= $this->render('partials/host/servicesummary.phtml') ?>
</div>
<?= $this->partial(
'list/services.phtml',
'monitoring',
array(
'compact' => true,
'showHost' => false,
'services' => $services,
'addColumns' => array()
)
); ?>

View File

@ -0,0 +1,151 @@
<?php
use Icinga\Module\Monitoring\Object\Service;
$self = $this;
if (! $this->compact): ?>
<div class="controls">
<?= $this->tabs; ?>
<?= $this->render('partials/service/object-header.phtml'); ?>
<h1><?= $this->translate('This Service\'s Event History'); ?></h1>
<?= $this->sortBox; ?>
<?= $this->limiter; ?>
<?= $this->paginator; ?>
<?= $this->filterEditor; ?>
</div>
<?php endif ?>
<div class="content">
<?php
if (count($history) === 0) {
echo $this->translate('No history events found matching the filter') . '</div>';
return;
}
?>
<?php
function contactsLink($match, $view) {
$links = array();
foreach (preg_split('/,\s/', $match[1]) as $contact) {
$links[] = $view->qlink(
$contact,
'monitoring/show/contact',
array('contact_name' => $contact),
array('title' => sprintf($view->translate('Show detailed information about %s'), $contact))
);
}
return '[' . implode(', ', $links) . ']';
}
?>
<table data-base-target="_next" class="action objecthistory">
<tbody>
<?php foreach ($history as $event): ?>
<?php
$stateClass = 'invalid';
switch ($event->type) {
case 'notify':
$icon = 'notification';
$title = $this->translate('Notification');
$stateClass = Service::getStateText($event->state);
$msg = preg_replace_callback(
'/^\[([^\]]+)\]/',
function($match) use ($self) { return contactsLink($match, $self); },
$this->escape($event->output)
);
break;
case 'comment':
$icon = 'comment';
$title = $this->translate('Comment');
$msg = $this->escape($event->output);
break;
case 'comment_deleted':
$icon = 'remove';
$title = $this->translate('Comment deleted');
$msg = $this->escape($event->output);
break;
case 'ack':
$icon = 'acknowledgement';
$title = $this->translate('Acknowledge');
$msg = $this->escape($event->output);
break;
case 'ack_deleted':
$icon = 'remove';
$title = $this->translate('Ack removed');
$msg = $this->escape($event->output);
break;
case 'dt_comment':
$icon = 'in_downtime';
$title = $this->translate('In Downtime');
$msg = $this->escape($event->output);
break;
case 'dt_comment_deleted':
$icon = 'remove';
$title = $this->translate('Downtime removed');
$msg = $this->escape($event->output);
break;
case 'flapping':
$icon = 'flapping';
$title = $this->translate('Flapping');
$msg = $this->escape($event->output);
break;
case 'flapping_deleted':
$icon = 'remove';
$title = $this->translate('Flapping stopped');
$msg = $this->escape($event->output);
break;
case 'hard_state':
$msg = '[ ' . $event->attempt . '/' . $event->max_attempts . ' ] ' . $this->escape($event->output);
$stateClass = Service::getStateText($event->state);
$icon = 'attention-alt';
$title = Service::getStateText($event->state);
break;
case 'soft_state':
$icon = 'spinner';
$msg = '[ ' . $event->attempt . '/' . $event->max_attempts . ' ] ' . $this->escape($event->output);
$stateClass = Service::getStateText($event->state);
$title = Service::getStateText($event->state);
break;
case 'dt_start':
$icon = 'downtime_start';
$title = $this->translate('Downtime Start');
$msg = $this->escape($event->output);
break;
case 'dt_end':
$icon = 'downtime_end';
$title = $this->translate('Downtime End');
$msg = $this->escape($event->output);
break;
}
?>
<tr class="state <?= $stateClass; ?>">
<td class="state">
<strong><?= $this->escape($title); ?></strong>
<br>
<?= date('d.m. H:i', $event->timestamp); ?>
</td>
<td><?php
$output = $this->tickets ? preg_replace_callback(
$this->tickets->getPattern(),
array($this->tickets, 'createLink'),
$msg
) : $msg;
?>
<?= sprintf(
$this->translate('%s on %s', 'Service running on host'),
$this->escape($event->service_display_name),
$event->host_display_name
) ?>
<br>
<div>
<?= $this->icon($icon, $title); ?> <?= empty($msg) ? '' : $msg; ?>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>

View File

@ -9,7 +9,7 @@
echo $this->qlink(
$this->translate('Send notification'),
'monitoring/host/send-custom-notification',
array('host_name' => $object->getName()),
array('host' => $object->getName()),
array(
'icon' => 'bell',
'data-base-target' => '_self',
@ -23,7 +23,7 @@
echo $this->qlink(
$this->translate('Send notification'),
'monitoring/service/send-custom-notification',
array('host_name' => $object->getHost()->getName(), 'service_description' => $object->getName()),
array('host' => $object->getHost()->getName(), 'service' => $object->getName()),
array(
'icon' => 'bell',
'data-base-target' => '_self',

View File

@ -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"

View File

@ -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),

View File

@ -93,6 +93,19 @@ abstract class MonitoredObjectController extends Controller
$this->view->object = $this->object;
}
/**
* Show the history for a host or service
*/
public function historyAction()
{
$this->getTabs()->activate('history');
$this->view->history = $this->object->fetchEventHistory()->eventhistory;
$this->setupLimitControl(50);
$this->setupPaginationControl($this->view->history, 50);
$this->view->object = $this->object;
}
/**
* Handle a command form
*
@ -145,6 +158,9 @@ abstract class MonitoredObjectController extends Controller
$params = array(
'host' => $object->getName()
);
if ($this->params->has('service')) {
$params['service'] = $this->params->get('service');
}
} else {
$isService = true;
$params = array(
@ -165,14 +181,14 @@ abstract class MonitoredObjectController extends Controller
'urlParams' => $params
)
);
if ($isService) {
if ($isService || $this->params->has('service')) {
$tabs->add(
'service',
array(
'title' => sprintf(
$this->translate('Show detailed information for service %s on host %s'),
$object->getName(),
$object->getHost()->getName()
$isService ? $object->getName() : $this->params->get('service'),
$isService ? $object->getHost()->getName() : $object->getName()
),
'label' => $this->translate('Service'),
'icon' => 'service',
@ -181,6 +197,19 @@ abstract class MonitoredObjectController extends Controller
)
);
}
$tabs->add(
'services',
array(
'title' => sprintf(
$this->translate('List all services on host %s'),
$isService ? $object->getHost()->getName() : $object->getName()
),
'label' => $this->translate('Services'),
'icon' => 'services',
'url' => 'monitoring/host/services',
'urlParams' => $params
)
);
if ($this->backend->hasQuery('eventhistory')) {
$tabs->add(
'history',
@ -195,7 +224,7 @@ abstract class MonitoredObjectController extends Controller
,
'label' => $this->translate('History'),
'icon' => 'rewind',
'url' => 'monitoring/show/history',
'url' => $isService ? 'monitoring/service/history' : 'monitoring/host/history',
'urlParams' => $params
)
);

View File

@ -1,5 +1,5 @@
Module: monitoring
Version: 2.0.0~alpha4
Version: 2.0.0-rc1
Description: Icinga monitoring module
This is the core module for most Icingaweb users. It provides an
abstraction layer for various Icinga data backends.

View File

@ -29,7 +29,7 @@ class ConfigCommand extends Command
*
* icingacli setup config directory
*
* icingacli setup config directory --mode 2775 --config /opt/icingaweb2/etc
* icingacli setup config directory --mode=2775 --config=/opt/icingaweb2/etc
*/
public function directoryAction()
{
@ -96,7 +96,7 @@ class ConfigCommand extends Command
*
* --path=<urlpath> The URL path to Icinga Web 2 [/icingaweb2]
*
* --root/--document-root=<directory> The directory from which the webserver will serve files [/path/to/icingaweb2/public]
* --root|--document-root=<directory> The directory from which the webserver will serve files [/path/to/icingaweb2/public]
*
* --config=<directory> Path to Icinga Web 2's configuration files [/etc/icingaweb2]
*
@ -106,9 +106,9 @@ class ConfigCommand extends Command
*
* icingacli setup config webserver apache
*
* icingacli setup config webserver apache --path /icingaweb2 --document-root /usr/share/icingaweb2/public --config=/etc/icingaweb2
* icingacli setup config webserver apache --path=/icingaweb2 --document-root=/usr/share/icingaweb2/public --config=/etc/icingaweb2
*
* icingacli setup config webserver apache --file /etc/apache2/conf.d/icingaweb2.conf
* icingacli setup config webserver apache --file=/etc/apache2/conf.d/icingaweb2.conf
*
* icingacli setup config webserver nginx
*/

View File

@ -0,0 +1,6 @@
Module: setup
Version: 2.0.0-rc1
Description: Setup module
Web based wizard for setting up Icinga Web 2 and its modules.
This includes the data backends (e.g. relational database, LDAP),
the authentication method, where to store the user preferences and much more.

View File

@ -41,7 +41,7 @@ class PhpCommand extends Command
*
* icingacli test php unit --verbose
* icingacli test php unit --build
* icingacli test php unit --include *SpecialTest
* icingacli test php unit --include=*SpecialTest
*/
public function unitAction()
{
@ -109,8 +109,8 @@ class PhpCommand extends Command
*
* icingacli test php style --verbose
* icingacli test php style --build
* icingacli test php style --include path/to/your/file
* icingacli test php style --exclude *someFile* --exclude someOtherFile*
* icingacli test php style --include=path/to/your/file
* icingacli test php style --exclude=*someFile* --exclude=someOtherFile*
*/
public function styleAction()
{

View File

@ -1,5 +1,5 @@
Module: test
Version: 2.0.0~alpha4
Version: 2.0.0-rc1
Description: Translation module
This module allows developers to run (unit) tests against Icinga Web 2 and
any of its modules. Usually you do not need to enable this.

View File

@ -1,7 +1,7 @@
Module: translation
Version: 2.0.0~alpha4
Version: 2.0.0-rc1
Description: Translation module
This module allows developers and translators to translate Icinga Web 2 and
its modules for multiple languages. You do not need this module to run an
internationalized web frontend. This is only for people who want to contribute
translations or translate just their own moduls.
translations or translate just their own modules.

View File

@ -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;

View File

@ -236,9 +236,6 @@ div.content.users {
}
div.controls div.user-header {
border-bottom: 2px solid @colorPetrol;
margin-bottom: 1em;
.user-name {
display: inline-block;
margin: 0 0 0.3em;
@ -302,9 +299,6 @@ div.content.groups {
}
div.controls div.group-header {
border-bottom: 2px solid @colorPetrol;
margin-bottom: 1em;
.group-name {
display: inline-block;
margin: 0 0 0.3em;