From: Robin Roevens <robin.roevens@disroot.org>
To: development@lists.ipfire.org
Subject: Re: [PATCH 4/4] [V2] zabbix_agentd: Add IPFire specific userparameters
Date: Tue, 13 Apr 2021 00:16:22 +0200 [thread overview]
Message-ID: <76b0ca2a3d864a703e808811ebe982160924234b.camel@disroot.org> (raw)
In-Reply-To: <D947EEC2-00AD-4DB1-A79A-652FFC137434@ipfire.org>
[-- Attachment #1: Type: text/plain, Size: 20107 bytes --]
Hi Michael
Michael Tremer schreef op ma 12-04-2021 om 11:36 [+0100]:
> Hello,
>
> > On 7 Apr 2021, at 21:44, Robin Roevens <robin.roevens(a)disroot.org>
> > wrote:
> > ...
> > ...
> > diff --git a/config/zabbix_agentd/ipfire_services.pl
> > b/config/zabbix_agentd/ipfire_services.pl
> > new file mode 100755
> > index 000000000..dbf8aec56
> > --- /dev/null
> > +++ b/config/zabbix_agentd/ipfire_services.pl
> > @@ -0,0 +1,221 @@
> > +#!/usr/bin/perl
> > +####################################################################
> > ###########
> > +# ipfire_services.pl - Retrieves available IPFire services
> > information and
> > +# return this as a JSON array suitable for easy
> > processing
> > +# by Zabbix server
> > +#
> > +# Author: robin.roevens (at) disroot.org
> > +# Version: 1.0
> > +#
> > +# Based on: services.cgi by IPFire Team
> > +# Copyright (C) 2007-2021 IPFire Team <info(a)ipfire.org>
> > +#
> > +# This program is free software: you can redistribute it and/or
> > modify
> > +# it under the terms of the GNU General Public License as published
> > by
> > +# the Free Software Foundation, either version 3 of the License, or
> > +# (at your option) any later version.
> > +#
> > +# This program is distributed in the hope that it will be useful,
> > +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > +# GNU General Public License for more details.
> > +#
> > +# You should have received a copy of the GNU General Public License
> > +# along with this program. If not, see <
> > http://www.gnu.org/licenses/>.
> > +#
> > +####################################################################
> > ###########
> > +
> > +use strict;
> > +
> > +# enable only the following on debugging purpose
> > +# use warnings;
> > +
> > +# Maps a nice printable name to the changing part of the pid file,
> > which
> > +# is also the name of the program
> > +my %servicenames =(
> > + 'DHCP Server' => 'dhcpd',
> > + 'Web Server' => 'httpd',
> > + 'CRON Server' => 'fcron',
> > + 'DNS Proxy Server' => 'unbound',
> > + 'Logging Server' => 'syslogd',
> > + 'Kernel Logging Server' => 'klogd',
> > + 'NTP Server' => 'ntpd',
> > + 'Secure Shell Server' => 'sshd',
> > + 'VPN' => 'charon',
> > + 'Web Proxy' => 'squid',
> > + 'Intrusion Detection System' => 'suricata',
> > + 'OpenVPN' => 'openvpn'
> > +);
>
> You have a hard-coded list with process names here. It technically is
> missing add-ons but including everything would make this list very long
> and a lot of work to maintain...
>
As explained in my previous mail, I don't currently know of a method to
get a list of mandatory IPFire specific services. So this list is a
straight copy out of services.cgi minus the translation.
The add-on services are looked up below, exactly like it is done in
services.cgi.
> > +
> > +# Hash to overwrite the process name of a process if it differs from
> > the launch command.
> > +my %overwrite_exename_hash = (
> > + "suricata" => "Suricata-Main"
> > +);
> > +
> > +my $first = 1;
> > +
> > +print "[";
> > +
> > +# Built-in services
> > +my $key = '';
> > +foreach $key (sort keys %servicenames){
> > + print "," if not $first;
> > + $first = 0;
> > +
> > + print "{";
> > + print "\"service\":\"$key\",";
> > +
> > + my $shortname = $servicenames{$key};
> > + print &servicestats($shortname);
> > +
> > + print "}";
> > +}
> > +
> > +# Generate list of installed addon pak's
> > +my @pak = `find /opt/pakfire/db/installed/meta-* 2>/dev/null | cut -
> > d"-" -f2`;
> > +foreach (@pak){
> > + chomp($_);
> > +
> > + # Check which of the paks are services
> > + my @svc = `find /etc/init.d/$_ 2>/dev/null | cut -d"/" -f4`;
> > + foreach (@svc){
> > + # blacklist some packages
> > + #
> > + # alsa has trouble with the volume saving and was not
> > really stopped
> > + # mdadm should not stopped with webif because this
> > could crash the system
> > + #
> > + chomp($_);
> > + if ( $_ eq 'squid' ) {
> > + next;
> > + }
> > + if ( ($_ ne "alsa") && ($_ ne "mdadm") ) {
>
> Another hardcoded list due to code duplication. Not your fault, but not
> pretty.
>
Indeed, as is probably clear by now, I too would prefer a more clean
way of generating a list of services.
Currently seeing this piece of code again.. I think this blacklist in
services.cgi only exists so the user can't stop/start the service using
the webgui.. But there is no such functionality in this script.. so I
think I can skip this blacklist, and also provide service state of alsa
and mdadm.. (not sure why squid is here, is there an addon that also
provides squid, maybe squid-accounting?)
> > + print ",";
> > + print "{";
> > +
> > + print "\"service\":\"Addon: $_\",";
> > + print "\"servicename\":\"$_\",";
> > +
> > + my $onboot = isautorun($_);
> > + print "\"onboot\":$onboot,";
> > +
> > + print &addonservicestats($_);
> > +
> > + print "}";
> > + }
> > + }
> > +}
> > +
> > +print "]";
> > +
> > +sub servicestats{
> > + my $cmd = $_[0];
> > + my $status = "\"servicename\":\"$cmd\",\"state\":\"0\"";
> > + my $pid = '';
> > + my $testcmd = '';
> > + my $exename;
> > + my $memory;
> > +
> > +
> > + $cmd =~ /(^[a-z]+)/;
> > +
> > + # Check if the exename needs to be overwritten.
> > + # This happens if the expected process name string
> > + # differs from the real one. This may happened if
> > + # a service uses multiple processes or threads.
> > + if (exists($overwrite_exename_hash{$cmd})) {
> > + # Grab the string which will be reported by
> > + # the process from the corresponding hash.
> > + $exename = $overwrite_exename_hash{$1};
> > + } else {
> > + # Directly expect the launched command as
> > + # process name.
> > + $exename = $1;
> > + }
> > +
> > + if (open(FILE, "/var/run/${cmd}.pid")){
> > + $pid = <FILE>; chomp $pid;
> > + close FILE;
> > + if (open(FILE, "/proc/${pid}/status")){
> > + while (<FILE>){
> > + if (/^Name:\W+(.*)/) {
> > + $testcmd = $1;
> > + }
> > + }
> > + close FILE;
> > + }
> > + if (open(FILE, "/proc/${pid}/status")) {
> > + while (<FILE>) {
> > + my ($key, $val) = split(":", $_, 2);
> > + if ($key eq 'VmRSS') {
> > + $val =~ /\s*([0-9]*)\s+kB/;
> > + # Convert kB to B
> > + $memory = $1*1024;
> > + last;
> > + }
> > + }
> > + close(FILE);
> > + }
> > + if ($testcmd =~ /$exename/){
> > + $status =
> > "\"servicename\":\"$cmd\",\"state\":1,\"pid\":$pid,\"memory\":$memory
> > ";
> > + }
> > + }
> > + return $status;
> > +}
> > +
> > +sub isautorun{
> > + my $cmd = $_[0];
> > + my $status = "0";
> > + my $init = `find /etc/rc.d/rc3.d/S??${cmd} 2>/dev/null`;
> > + chomp ($init);
> > + if ($init ne ''){
> > + $status = "1";
> > + }
> > + $init = `find /etc/rc.d/rc3.d/off/S??${cmd} 2>/dev/null`;
> > + chomp ($init);
> > + if ($init ne ''){
> > + $status = "0";
> > + }
> > +
> > + return $status;
> > +}
> > +
> > +sub addonservicestats{
> > + my $cmd = $_[0];
> > + my $status = "0";
> > + my $pid = '';
> > + my $testcmd = '';
> > + my $exename;
> > + my @memory = (0);
> > +
> > + $testcmd = `sudo /usr/local/bin/addonctrl $_ status
> > 2>/dev/null`;
> > +
> > + if ( $testcmd =~ /is\ running/ && $testcmd !~ /is\ not\
> > running/){
> > + $status = "\"state\":1";
> > +
> > + $testcmd =~ s/.* //gi;
> > + $testcmd =~ s/[a-z_]//gi;
> > + $testcmd =~ s/\[[0-1]\;[0-9]+//gi;
> > + $testcmd =~ s/[\(\)\.]//gi;
> > + $testcmd =~ s/ //gi;
> > + $testcmd =~ s/^[//gi;
> > +
> > + my @pid = split(/\s/,$testcmd);
> > + $status .=",\"pid\":\"$pid[0]\"";
> > +
> > + my $memory = 0;
> > +
> > + foreach (@pid){
> > + chomp($_);
> > + if (open(FILE, "/proc/$_/statm")){
> > + my $temp = <FILE>;
> > + @memory = split(/ /,$temp);
> > + }
> > + $memory+=$memory[0];
> > + }
> > + $memory*=1024;
> > + $status .=",\"memory\":$memory";
> > + }else{
> > + $status = "\"state\":0";
> > + }
> > + return $status;
> > +}
> > diff --git a/config/zabbix_agentd/sudoers
> > b/config/zabbix_agentd/sudoers
> > index 1b362a4fd..340bb8e66 100644
> > --- a/config/zabbix_agentd/sudoers
> > +++ b/config/zabbix_agentd/sudoers
> > @@ -14,4 +14,4 @@
> > # Append / edit the following list of commands to fit your needs:
> > #
> > Defaults:zabbix !requiretty
> > -zabbix ALL=(ALL) NOPASSWD: /opt/pakfire/pakfire status
> > +zabbix ALL=(ALL) NOPASSWD: /opt/pakfire/pakfire status,
> > /usr/local/bin/addonctrl, /sbin/iptables, /usr/sbin/fping
>
> This is absolutely impossible to give the zabbix user control to
> start/stop any add-on and to read and change the entire iptables
> ruleset.
>
> Zabbix is listening on the network and does not have any kind of
> authentication. Any security vulnerabilities in Zabbix would allow to
> alter the firewall rules and DoS any add-ons. This is dangerous.
There is some level of security:
- the agent will only respond on requests from the configured Zabbix
server (which the user has to configure in the zabbix agentd conf-file,
and defaults to 127.0.0.1 as we can't possibly know the ip of the
user's server, so by default the agent responds to no one)
- By default remote commands (as in requests to the agent to execute
any command given) are disabled so the only way to make the agent call
those binaries is to request a predefined userparameter that in turn
will execute such a command with predefined params (see the additional
template_...conf-files)
and on top of that, the user can add additional security by configuring
encryption in the agent main configfile
But as you point out, a possible vulnerability of some sort may indeed
still pose risks this way.
>
> Are there any alternative ways to realise this functionality?
As sudo is not able to allow commands with variable parameters together
with fixed parameters, a popular solution is using wrapper scripts, not
writable by the user, denying the user direct access to the commands in
question.
/usr/local/bin/addonctrl is only called from within the
ipfire_services.pl script I provide, in the form of "addonctrl
<service> status".
So I could add /etc/zabbix_agentd/scripts/ipfire_services.pl to the
sudo list instead so that the zabbix user won't be able to call
addonctrl directly. (making sure zabbix user has no write permission on
that script)
The same goes for iptables. This is only called by the predefined
userparameter "ipfire.net.fw.hits[*]" defined in
template_module_ipfire_network_stats.conf in the form "/sbin/iptables -
vnxL $1 | grep "\/\* $2 \*\/" | awk '{ print $$2 }';" where $1 (chain)
and $2 (rule) are parameters that have to be supplied to the agent when
requesting that item.
Here I can also supply a wrapper-script to be called by sudo
/etc/zabbix_agentd/scripts/firewall_hits.sh that accepts those
parameters, and checks them for validity and against abuse.
Regards
Robin
>
> > diff --git
> > a/config/zabbix_agentd/template_module_ipfire_network_stats.conf
> > b/config/zabbix_agentd/template_module_ipfire_network_stats.conf
> > new file mode 100644
> > index 000000000..f1658ed07
> > --- /dev/null
> > +++ b/config/zabbix_agentd/template_module_ipfire_network_stats.conf
> > @@ -0,0 +1,4 @@
> > +### Parameters for monitoring IPFire network statistics
> > +UserParameter=ipfire.net.gateway.pingtime,sudo /usr/sbin/fping -c 3
> > gateway 2>&1 | tail -n 1 | awk '{print $NF}' | cut -d '/' -f2
> > +UserParameter=ipfire.net.gateway.ping,sudo /usr/sbin/fping -q -r 3
> > gateway; [ ! $? ]; echo $?
> > +UserParameter=ipfire.net.fw.hits[*],sudo /sbin/iptables -vnxL $1 |
> > grep "\/\* $2 \*\/" | awk '{ print $$2 }';
> > diff --git
> > a/config/zabbix_agentd/template_module_ipfire_services.conf
> > b/config/zabbix_agentd/template_module_ipfire_services.conf
> > new file mode 100644
> > index 000000000..5f95218e3
> > --- /dev/null
> > +++ b/config/zabbix_agentd/template_module_ipfire_services.conf
> > @@ -0,0 +1,2 @@
> > +### Parameter for monitoring IPFire services
> > +UserParameter=ipfire.services,/etc/zabbix_agentd/scripts/ipfire_serv
> > ices.pl
> > diff --git a/lfs/zabbix_agentd b/lfs/zabbix_agentd
> > index 73e08d20a..c0d28d51f 100644
> > --- a/lfs/zabbix_agentd
> > +++ b/lfs/zabbix_agentd
> > @@ -33,7 +33,7 @@ DIR_APP = $(DIR_SRC)/$(THISAPP)
> > TARGET = $(DIR_INFO)/$(THISAPP)
> > PROG = zabbix_agentd
> > PAK_VER = 5
> > -DEPS =
> > +DEPS = "fping"
> >
> > #####################################################################
> > ##########
> > # Top-level Rules
> > @@ -97,6 +97,12 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
> > /etc/zabbix_agentd/zabbix_agentd.conf.ipfirenew
> > install -v -m 644
> > $(DIR_SRC)/config/zabbix_agentd/template_app_pakfire.conf \
> > /etc/zabbix_agentd/zabbix_agentd.d/template_app_pakfi
> > re.conf.ipfirenew
> > + install -v -m 644
> > $(DIR_SRC)/config/zabbix_agentd/template_module_ipfire_network_stats.
> > conf \
> > + /etc/zabbix_agentd/zabbix_agentd.d/template_module_ip
> > fire_network_stats.conf.ipfirenew
> > + install -v -m 644
> > $(DIR_SRC)/config/zabbix_agentd/template_module_ipfire_services.conf
> > \
> > + /etc/zabbix_agentd/zabbix_agentd.d/template_module_ip
> > fire_services.conf.ipfirenew
> > + install -v -m 755
> > $(DIR_SRC)/config/zabbix_agentd/ipfire_services.pl \
> > + /etc/zabbix_agentd/scripts/ipfire_services.pl.ipfiren
> > ew
> >
> > # Create directory for additional agent modules
> > -mkdir -pv /usr/lib/zabbix
> > diff --git a/src/paks/zabbix_agentd/install.sh
> > b/src/paks/zabbix_agentd/install.sh
> > index 4248a7ec1..ced915c81 100644
> > --- a/src/paks/zabbix_agentd/install.sh
> > +++ b/src/paks/zabbix_agentd/install.sh
> > @@ -66,8 +66,13 @@ restore_backup ${NAME}
> > # Put zabbix configfiles in place
> > setup_configfile /etc/zabbix_agentd/zabbix_agentd.conf
> > setup_configfile
> > /etc/zabbix_agentd/zabbix_agentd.d/template_app_pakfire.conf
> > +setup_configfile
> > /etc/zabbix_agentd/zabbix_agentd.d/template_module_ipfire_network_sta
> > ts.conf
> > +setup_configfile
> > /etc/zabbix_agentd/zabbix_agentd.d/template_module_ipfire_services.co
> > nf
> > setup_configfile /etc/sudoers.d/zabbix
> >
> > +# Overwrite script if it exists as user should not modify it but it
> > is included in backup
> > +mv /etc/zabbix_agentd/scripts/ipfire_services.pl.ipfirenew
> > /etc/zabbix_agentd/scripts/ipfire_services.pl
> > +
> > if $review_required; then
> > echo "WARNING: New versions of some configfile(s) where
> > provided as .ipfirenew-files."
> > echo " They may need manual review in order to take
> > advantage of new features"
> > diff --git a/src/paks/zabbix_agentd/uninstall.sh
> > b/src/paks/zabbix_agentd/uninstall.sh
> > index 7a13880c5..ccbc8f7cf 100644
> > --- a/src/paks/zabbix_agentd/uninstall.sh
> > +++ b/src/paks/zabbix_agentd/uninstall.sh
> > @@ -26,6 +26,8 @@ stop_service ${NAME}
> >
> > # Remove .ipfirenew files in advance so they won't be included in
> > backup
> > rm -rfv /etc/zabbix_agentd/*.ipfirenew
> > /etc/zabbix_agentd/*/*.ipfirenew
> > +# Remove script-file as it should not have been modified by user
> > +rm -fv /etc/zabbix_agentd/scripts/ipfire_services.pl
> >
> > make_backup ${NAME}
> > remove_files
> > --
> > 2.30.2
> >
> >
> > --
> > Dit bericht is gescanned op virussen en andere gevaarlijke
> > inhoud door MailScanner en lijkt schoon te zijn.
> >
>
> -Michael
>
>
--
Dit bericht is gescanned op virussen en andere gevaarlijke
inhoud door MailScanner en lijkt schoon te zijn.
next prev parent reply other threads:[~2021-04-12 22:16 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-04-07 20:44 [PATCH 0/4] [V2] zabbix_agentd: new maintainer/summary Robin Roevens
2021-04-07 20:44 ` [PATCH 1/4] [V2] zabbix_agentd: Update to v5.0.10 (LTS) Robin Roevens
2021-04-09 19:25 ` Adolf Belka
2021-04-10 21:05 ` Robin Roevens
2021-04-12 10:27 ` Michael Tremer
2021-04-12 11:23 ` Adolf Belka
2021-04-12 13:48 ` Michael Tremer
2021-04-12 10:26 ` Michael Tremer
2021-04-07 20:44 ` [PATCH 2/4] [V2] zabbix_agentd: Fix agent modules directory Robin Roevens
2021-04-09 19:36 ` Adolf Belka
2021-04-10 21:13 ` Robin Roevens
2021-04-12 10:26 ` Michael Tremer
2021-04-12 10:50 ` Robin Roevens
2021-04-12 10:52 ` Michael Tremer
2021-04-12 11:38 ` Robin Roevens
2021-04-12 13:45 ` Michael Tremer
2021-04-07 20:44 ` [PATCH 3/4] [V2] zabbix_agentd: Better configfile handling during update Robin Roevens
2021-04-07 20:44 ` [PATCH 4/4] [V2] zabbix_agentd: Add IPFire specific userparameters Robin Roevens
2021-04-12 10:36 ` Michael Tremer
2021-04-12 22:16 ` Robin Roevens [this message]
2021-04-15 11:21 ` Michael Tremer
2021-04-15 13:12 ` Robin Roevens
2021-04-15 20:34 ` Robin Roevens
2021-04-19 13:42 ` Michael Tremer
2021-04-19 13:37 ` Michael Tremer
2021-04-19 20:50 ` Robin Roevens
2021-04-12 10:32 ` [PATCH 0/4] [V2] zabbix_agentd: new maintainer/summary Michael Tremer
2021-04-12 21:19 ` Robin Roevens
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=76b0ca2a3d864a703e808811ebe982160924234b.camel@disroot.org \
--to=robin.roevens@disroot.org \
--cc=development@lists.ipfire.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox