enhance radius plugin

This commit is contained in:
qgarnier 2017-07-27 15:55:05 +02:00
parent a4c0f293d2
commit 75e05e62ad
1 changed files with 216 additions and 53 deletions

View File

@ -20,13 +20,85 @@
package apps::protocols::radius::mode::login; package apps::protocols::radius::mode::login;
use base qw(centreon::plugins::mode); use base qw(centreon::plugins::templates::counter);
use strict; use strict;
use warnings; use warnings;
use Time::HiRes qw(gettimeofday tv_interval); use Time::HiRes qw(gettimeofday tv_interval);
use Authen::Radius; use Authen::Radius;
my $instance_mode;
my $radius_result_attributes = {};
sub custom_status_threshold {
my ($self, %options) = @_;
my $status = 'ok';
my $message;
eval {
local $SIG{__WARN__} = sub { $message = $_[0]; };
local $SIG{__DIE__} = sub { $message = $_[0]; };
if (defined($instance_mode->{option_results}->{critical_status}) && $instance_mode->{option_results}->{critical_status} ne '' &&
eval "$instance_mode->{option_results}->{critical_status}") {
$status = 'critical';
} elsif (defined($instance_mode->{option_results}->{warning_status}) && $instance_mode->{option_results}->{warning_status} ne '' &&
eval "$instance_mode->{option_results}->{warning_status}") {
$status = 'warning';
}
};
if (defined($message)) {
$self->{output}->output_add(long_msg => 'filter status issue: ' . $message);
}
return $status;
}
sub custom_status_output {
my ($self, %options) = @_;
my $msg = 'Radius Access Request Status: ' . $self->{result_values}->{status} .
' [error msg: ' . $self->{result_values}->{error_msg} . ']';
return $msg;
}
sub custom_status_calc {
my ($self, %options) = @_;
$self->{result_values}->{status} = $options{new_datas}->{$self->{instance} . '_status'};
$self->{result_values}->{error_msg} = $options{new_datas}->{$self->{instance} . '_error_msg'};
$self->{result_values}->{attributes} = $radius_result_attributes;
return 0;
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'radius', type => 0, message_separator => ' - ' },
];
$self->{maps_counters}->{radius} = [
{ label => 'status', threshold => 0, set => {
key_values => [ { name => 'status' }, { name => 'error_msg' } ],
closure_custom_calc => $self->can('custom_status_calc'),
closure_custom_output => $self->can('custom_status_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => $self->can('custom_status_threshold'),
}
},
{ label => 'time', set => {
key_values => [ { name => 'elapsed' } ],
output_template => 'Response time : %.3f second(s)',
perfdatas => [
{ label => 'time', value => 'elapsed_absolute', template => '%.3f',
min => 0, unit => 's' },
],
}
},
];
}
sub new { sub new {
my ($class, %options) = @_; my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options); my $self = $class->SUPER::new(package => __PACKAGE__, %options);
@ -36,81 +108,141 @@ sub new {
$options{options}->add_options(arguments => $options{options}->add_options(arguments =>
{ {
"hostname:s" => { name => 'hostname' }, "hostname:s" => { name => 'hostname' },
"port:s" => { name => 'port', default => 1812 },
"secret:s" => { name => 'secret' }, "secret:s" => { name => 'secret' },
"username:s" => { name => 'username' }, "username:s" => { name => 'username' },
"password:s" => { name => 'password' }, "password:s" => { name => 'password' },
"warning:s" => { name => 'warning' }, "warning:s" => { name => 'warning' },
"critical:s" => { name => 'critical' }, "critical:s" => { name => 'critical' },
"timeout:s" => { name => 'timeout', default => '30' }, "timeout:s" => { name => 'timeout', default => 5 },
"retry:s" => { name => 'retry', default => 0 },
'radius-attribute:s%' => { name => 'radius_attribute' },
'radius-dictionary:s' => { name => 'radius_dictionary' },
"warning-status:s" => { name => 'warning_status', default => '' },
"critical-status:s" => { name => 'critical_status', default => '%{status} ne "accepted"' },
}); });
return $self; return $self;
} }
sub check_options { sub check_options {
my ($self, %options) = @_; my ($self, %options) = @_;
$self->SUPER::init(%options); $self->SUPER::check_options(%options);
if (($self->{perfdata}->threshold_validate(label => 'warning', value => $self->{option_results}->{warning})) == 0) { my @mandatory = ('hostname', 'secret');
$self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'."); push @mandatory, 'username', 'password' if (!defined($self->{option_results}->{radius_attribute}));
foreach (@mandatory) {
if (!defined($self->{option_results}->{$_})) {
$self->{output}->add_option_msg(short_msg => "Please set the " . $_ . " option");
$self->{output}->option_exit();
}
}
if (defined($self->{option_results}->{radius_attribute}) &&
(!defined($self->{option_results}->{radius_dictionary}) || $self->{option_results}->{radius_dictionary} eq '')) {
$self->{output}->add_option_msg(short_msg => "Please set radius-dictionary option");
$self->{output}->option_exit(); $self->{output}->option_exit();
} }
if (($self->{perfdata}->threshold_validate(label => 'critical', value => $self->{option_results}->{critical})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $self->{option_results}->{critical} . "'."); $self->{option_results}->{retry} = 0 if (!defined($self->{option_results}->{retry}) || $self->{option_results}->{retry} !~ /^\d+$/);
$self->{output}->option_exit(); if (defined($self->{option_results}->{port}) && $self->{option_results}->{port} =~ /^\d+$/) {
$self->{option_results}->{hostname} .= ':' . $self->{option_results}->{port};
} }
$instance_mode = $self;
$self->change_macros();
}
if (!defined($self->{option_results}->{hostname})) { sub change_macros {
$self->{output}->add_option_msg(short_msg => "Please set the hostname option"); my ($self, %options) = @_;
$self->{output}->option_exit();
}
if (!defined($self->{option_results}->{secret})) { foreach (('warning_status', 'critical_status')) {
$self->{output}->add_option_msg(short_msg => "Please set the secret option"); if (defined($self->{option_results}->{$_})) {
$self->{output}->option_exit(); $self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$self->{result_values}->{$1}/g;
} }
if (!defined($self->{option_results}->{username})) {
$self->{output}->add_option_msg(short_msg => "Please set the username option");
$self->{output}->option_exit();
}
if (!defined($self->{option_results}->{password})) {
$self->{output}->add_option_msg(short_msg => "Please set the password option");
$self->{output}->option_exit();
} }
} }
sub run { sub radius_simple_connection {
my ($self, %options) = @_; my ($self, %options) = @_;
my $timing0 = [gettimeofday]; $self->{timing0} = [gettimeofday];
my $retry = 0;
my $radius = Authen::Radius->new(Host => $self->{option_results}->{hostname}, while ($retry <= $self->{option_results}->{retry}) {
Secret => $self->{option_results}->{secret}, if ($self->{radius_session}->check_pwd($self->{option_results}->{username}, $self->{option_results}->{password})) {
TimeOut => $self->{option_results}->{timeout}, $self->{radius}->{status} = 'accepted';
); last;
}
if ($retry + 1 > $self->{option_results}->{retry}) {
my $authentication = $radius->check_pwd($self->{option_results}->{username}, $self->{option_results}->{password}); $self->{radius}->{status} = 'rejected';
$self->{radius}->{error_msg} = $self->{radius_session}->strerror();
if ($authentication != 1) { }
$self->{output}->output_add(severity => 'CRITICAL', $retry++;
short_msg => 'Authentication failed');
} }
}
my $timeelapsed = tv_interval ($timing0, [gettimeofday]); sub radius_attr_connection {
my ($self, %options) = @_;
my $exit = $self->{perfdata}->threshold_check(value => $timeelapsed, my $message;
threshold => [ { label => 'critical', 'exit_litteral' => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]); eval {
$self->{output}->output_add(severity => $exit, local $SIG{__WARN__} = sub { $message = join(' - ', @_); };
short_msg => sprintf("Response time %.3f second(s)", $timeelapsed)); local $SIG{__DIE__} = sub { $message = join(' - ', @_); };
$self->{output}->perfdata_add(label => "time", unit => 's',
value => sprintf('%.3f', $timeelapsed),
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'));
$self->{output}->display(); Authen::Radius->load_dictionary($self->{option_results}->{radius_dictionary});
$self->{output}->exit(); foreach (keys %{$self->{option_results}->{radius_attribute}}) {
$self->{radius_session}->add_attributes({ Name => $_, Value => $self->{option_results}->{radius_attribute}->{$_} });
}
};
if (defined($message)) {
$self->{output}->output_add(long_msg => $message, debug => 1);
$self->{output}->add_option_msg(short_msg => "Issue with dictionary and attributes");
$self->{output}->option_exit();
}
$self->{timing0} = [gettimeofday];
my $retry = 0;
while ($retry <= $self->{option_results}->{retry}) {
my $type;
if ($self->{radius_session}->send_packet(ACCESS_REQUEST) && ($type = $self->{radius_session}->recv_packet()) == ACCESS_ACCEPT) {
$self->{radius}->{status} = 'accepted';
last;
}
if ($retry + 1 > $self->{option_results}->{retry}) {
$self->{radius}->{status} = 'unknown';
$self->{radius}->{error_msg} = $self->{radius_session}->strerror();
if (defined($type) && $type == ACCESS_REJECT) {
$self->{radius}->{status} = 'rejected';
}
}
$retry++;
}
}
sub manage_selection {
my ($self, %options) = @_;
$self->{radius} = { status => 'unknown', error_msg => 'none' };
$self->{radius_session} = Authen::Radius->new(
Host => $self->{option_results}->{hostname},
Secret => $self->{option_results}->{secret},
TimeOut => $self->{option_results}->{timeout},
);
if (defined($self->{option_results}->{radius_attribute})) {
$self->radius_attr_connection();
} else {
$self->radius_simple_connection();
}
$self->{radius}->{elapsed} = tv_interval($self->{timing0}, [gettimeofday]);
foreach my $attr ($self->{radius_session}->get_attributes()) {
$radius_result_attributes->{$attr->{Name}} = defined($attr->{Value}) ? $attr->{Value} : '';
$self->{output}->output_add(long_msg => 'Attribute Name = ' . $attr->{Name} .
', Value = ' . (defined($attr->{Value}) ? $attr->{Value} : ''), debug => 1);
}
} }
1; 1;
@ -119,7 +251,10 @@ __END__
=head1 MODE =head1 MODE
Check Connection (also login) to a Radius Server. Check login to a Radius Server.
Example with attributes:
centreon_plugins.pl --plugin=apps/protocols/radius/plugin.pm --mode=login --hostname=192.168.1.2 --secret=centreon --radius-attribute='User-Password=test' --radius-attribute='User-Name=user@test.com' --radius-dictionary=dictionary.txt
=over 8 =over 8
@ -127,6 +262,10 @@ Check Connection (also login) to a Radius Server.
IP Addr/FQDN of the radius host IP Addr/FQDN of the radius host
=item B<--port>
Radius port (Default: 1812)
=item B<--secret> =item B<--secret>
Secret of the radius host Secret of the radius host
@ -141,13 +280,37 @@ Specify password for authentication
=item B<--timeout> =item B<--timeout>
Connection timeout in seconds (Default: 30) Connection timeout in seconds (Default: 5)
=item B<--warning> =item B<--timeout>
Number of retry connection (Default: 0)
=item B<--radius-attribute>
If you need to add option, please following attributes.
Option username and password should be set with that option.
Example: --radius-attribute="User-Password=test"
=item B<--radius-dictionary>
Set radius-dictionary file (mandatory with --radius-attribute).
=item B<--warning-status>
Set warning threshold for status (Default: '').
Can used special variables like: %{status}, %{error_msg}, %{attributes}.
=item B<--critical-status>
Set critical threshold for status (Default: '%{status} ne "accepted"').
Can used special variables like: %{status}, %{error_msg}, %{attributes}.
=item B<--warning-time>
Threshold warning in seconds Threshold warning in seconds
=item B<--critical> =item B<--critical-time>
Threshold critical in seconds Threshold critical in seconds