Secure Arguments

Overview Copied

Secure Arguments (also known as secure args) is a mechanism in Opsview that enhances the security of plugins, notification scripts, and event handlers by passing their arguments through standard input (stdin) instead of the command line.

When Opsview calls plugins, notification scripts, and event handlers, it requires providing arguments to the script. Traditionally, these executable files accept parameters via command line arguments and in some cases, environment variables. This provides flexibility and interoperability between different platforms and compatibility with Nagios plugins. However, these traditional command line argument scripts can be a potential security risk as these are visible to all local operating system users on the collectors.

To address this, Opsview supports secure args scripts to protect sensitive information in arguments. When secure args are used, the arguments will not be visible in the process list on the collectors and cannot be intercepted by local users on the same host where they are executed. This enhances security while providing a simple and flexible execution pattern for plugins, notification scripts, and event handlers.

Note

Not all of Opsview’s built-in plugins support secure args. Custom plugins, notification scripts, and event handlers will need to be modified and re-imported.

To determine how plugins or scripts are executed, Opsview tracks an execution style for each. Uploaded plugins or scripts can be executed in the following ways:

The Execution Style column on the Configuration > Monitoring Plugins page lists the value for each plugin. The execution style of built-in plugins or scripts cannot be modified and is controlled by the system (displayed as Auto).

Use secure args scripts Copied

For a script to accept secure args, it must read a line from stdin, decode it as a JSON dictionary, and extract the array of strings from the cmd key within that dictionary. This array of strings can then replace the command line arguments in the existing argv variable. The script can then continue its execution as normal.

To test a script, you can manually pass the JSON string via stdin after constructing the argv array:

echo '{"cmd": ["arg1", "arg2", "arg3", "-a", "arg4", "--example", "arg5"]}' | ./my_secure_args_script.py

Alternatively, you can use the following helper script on an Opsview system when running commands manually, which accepts command line arguments without any need for further changes.

./my_secure_args_script.py <<< $(/opt/opsview/monitoringscripts/commands/ov_cmd_split arg1 arg2 arg3 -a arg4 --example arg5)

The /opt/opsview/monitoringscripts/commands/ov_cmd_split script is available for the opsview user with the ov_cmd_split alias.

Import secure args scripts Copied

For plugins, you can import the secure args scripts using the UI.

  1. Go to the Configuration > Monitoring Plugins and click Import.
  2. Select the Compatible with Secure Args execution style checkbox.

For more details, see User Interface.

Import Monitoring Plugins window

Note

Import Monitoring Plugins functionality is disabled by default on new installations. For more information, see documentation on Active Checks and Monitoring plugins.

For a notification script or event handler, set the execution style as STDIN_ARGS instead of COMMAND_LINE_ARGS during import. This passes the arguments via stdin and not the command line.

sudo -u opsview /opt/opsview/orchestrator/bin/orchestratorimportscripts -e STDIN_ARGS notifications /path/to/notificationscript
sudo -u opsview /opt/opsview/orchestrator/bin/orchestratorimportscripts -e STDIN_ARGS eventhandlers /path/to/script

Import an Opspack with secure args plugins Copied

To upload plugins in an Opspack, the config.json file allows an optional section of the following format:

"plugin_execution_style": {
  "<plugin name>": "<COMMAND_LINE_ARGS|STDIN_ARGS>"
}

This will be used to set the execution style on a per-plugin basis. For example, the following config.json sets the check_mssql_database.py plugin to use secure args instead of command line args.

{
   "plugin_execution_style": {
      "check_mssql_database.py": "STDIN_ARGS"
   },
   ...
}

If plugin_execution_style is not specified, the default style is COMMAND_LINE_ARGS. This can be imported as a normal Opspack on the Configuration > Host Templates page.

Note

Import Opspack functionality is disabled by default on new installations. For more information, see documentation on Importing Opspacks.

Code samples Copied

The following code examples show how to implement secure args scripts in several common programming languages.

Python with plugnpy Copied

Using the Opsview plugnpy Python library (included with /opt/opsview/python3/bin/python on Opsview systems):

#!/opt/opsview/python3/bin/python

import sys
from plugnpy import Parser as PnPParser, ExecutionStyle, Check


def get_args():
    parser = PnPParser(
        execution_style=ExecutionStyle.STDIN_ARGS,  # switches PnP parser to use STDIN-only for args
    )
    # Add arguments here:
    # e.g.
    parser.add_argument('-a', '--arg1', help="An example argument")
    return parser.parse_args(sys.argv[1:])


def main():
    args = get_args()
    check = Check()
    # Implement check logic and add metrics here
    # e.g.
    check.add_metric(
        name="metric_name",
        value=0,
        unit="",
        warning_threshold=0,
        critical_threshold=0,
        display_format=None,
        display_in_perf=True,
        display_in_summary=True,
        display_name=None,
        convert_metric=None,
        si_bytes_conversion=False,
        summary_precision=2,
        perf_data_precision=2,
        message="All OK",
    )
    check.final()


if __name__ == "__main__":
    main()

Python without plugnpy Copied

#!/opt/opsview/python3/bin/python

import os
import sys
import argparse
import json
import stat


def get_args():
    has_cmdline_args = len(sys.argv) > 1
    has_empty_cmdline_args = not any(sys.argv[1:])

    # Check if we should be using STDIN
    # If STDIN is available, we only care if we have a PIPE or FILE redirection
    try:
        fd = sys.stdin.fileno()
        stat_data = os.fstat(fd)
        uses_stdin = stat.S_ISREG(stat_data.st_mode) or stat.S_ISFIFO(stat_data.st_mode)
    except (OSError, AttributeError, TypeError):
        uses_stdin = False

    if uses_stdin:
        data = sys.stdin.readlines()
        try:
            arg_string = data[0].strip()
            args = json.loads(arg_string)['cmd']
        except IndexError:
            uses_stdin = False
    if not uses_stdin:
        args = sys.argv[1:]
        if (not has_cmdline_args) or has_empty_cmdline_args or ('-h' in args) or ('--help' in args):
            print(
                "WARNING: This script supports secure args passing via STDIN. "
                "You have passed arguments on the command line. "
                "Please update your configuration to pass secure args via STDIN."
            )

    parser = argparse.ArgumentParser()
    # Add arguments here:
    # e.g.
    parser.add_argument('-a', '--arg1', help="An example argument")
    return parser.parse_args(args=args)


def main():
    args = get_args()
    # Implement check logic and add metrics here
    # e.g.
    print(args)
    status = 0
    return status


if __name__ == "__main__":
    sys.exit(main())

Perl Copied

#!/bin/perl
use warnings; 
use strict;
use Fcntl ':mode'; # Import stat constants
use JSON qw( decode_json );

my $cmd                  = [];
my $has_cmdline_args     = scalar(@ARGV) > 0;
my $has_empty_cmdline_args =
    scalar(@ARGV) == 0 || grep { $_ eq '' } @ARGV; # no args or empty args

# Fetch the mode of STDIN to determine if it is a pipe or a file
#  If it's a file, we also check if it's been pre-closed
my $stdin_mode = ( stat(STDIN) )[2];
my $uses_stdin =
    S_ISFIFO($stdin_mode) || ( S_ISREG($stdin_mode) && !eof(STDIN) );


 if ($uses_stdin) {
    my $line = <STDIN> // '';
    $line =~ s/^\s+|\s+$//g; # Trim whitespace
    chomp($line);
    my $json_data;
    eval {
        $json_data = decode_json($line);
        1;
    } or do {
        my $error = $@ || 'Unknown error';
        print "Failed to parse args: $error\n";
        exit 3;
    };
    $cmd = $json_data->{'cmd'} // [];
}
else {
    if (  !$has_cmdline_args
        || $has_empty_cmdline_args
        || grep { $_ eq '-h' || $_ eq '--help' } @ARGV )
    {

        print "WARNING: This script supports secure args passing via STDIN. ",
            "You have passed arguments on the command line. ",
            "Please update your configuration to pass secure args via STDIN.\n";
    }
}

@ARGV = @$cmd if @$cmd;

# Implement script using @ARGV as normal
print "Args: @ARGV\n";

exit 0;

BASH Copied

#!/bin/bash
# Requires jq and bash 4

if [ $# -eq 0 ]; then
  read -r json;
  readarray -t args < <(echo $json | jq -rc '.cmd[]')
else
  args=("$@")
  if [[ " ${args[*]} " =~ [[:space:]]--help[[:space:]] || " ${args[*]} " =~ [[:space:]]-h[[:space:]] ]]; then
    echo "Usage: help text goes here..."
  else
    echo "WARNING: This script supports secure args passing via STDIN."
    echo "You have passed arguments on the command line."
    echo "Please update your configuration to pass secure args via STDIN."
  fi 
fi

set -- "${args[@]}"

# Implement script as usual using $@
echo "Args: $@"

exit 0
["Opsview On-Premises"] ["User Guide"]

Was this topic helpful?