[Solved-ish] Collectd-mod-exec: execute script as root

I am currently trying to get DSL modem statistics into collectd, using a custom script called by the collectd-mod-exec. I fully understand that, to mitigate security concerns, collectd refuses to run scripts as root. However, this presents several problems:

In order to retrieve DSL statistics, or more specifically data from the DSL modem, I need to run the following (transcribed from /lib/functions/lantiq_dsl.sh):

killall -q -0 ${XDSL_CTRL} && (
	lock /var/lock/dsl_pipe
	echo "$@" > /tmp/pipe/dsl_cpe0_cmd
	cat /tmp/pipe/dsl_cpe0_ack
	lock -u /var/lock/dsl_pipe
)

(note: $XDSL_CTRL is either vdsl_cpe_control or, on older versions of OpenWrt, dsl_cpe_control, $@ is the actual command sent to the DSL modem.)

Now, when run as non-root user, this will obviously fail on several levels:

  • killall requires root to kill vdsl_cpe_control, so one could forego this in a good-faith assumption it is currently not running. Not great, but there's just no way to kill anything as non-root
  • lock /var/lock/dsl_pipe fails because /var/lock is only writeable to root. One could mitigate this to make /var/lock writeable to others. Again, not great.
  • but then, echoing to /tmp/pipe/dsl_cpe0_cmd will finally fail because this, again, is created by the root user and not writeable by others
  • (finally, even if one would chmod o+w /var/lock, lock -u on any lock there fails as user nobody and honestly, I have no idea why)

I am at a loss how all of this can be reformed so it would work as non-root user. The only way I see is to make an exception and ask collectd to run a script as root. Is there any way to do this?

Or does someone have another, better idea?

(I am thinking about running a cronjob as root that creates the collectd values on a regular basis, writes them to a temporary file, and just have the script called by collectd retrieve it, but that seems ... not so great.)

        config_get cmduser "$cfg" cmduser                                                                                        
        config_get cmdgroup "$cfg" cmdgroup

Thank you for your reply. It's very terse, but I assume you are talking about configuring the exec plugin to run as root?

As I said, collectd, or at least the exec plugin, refuses to run scripts as root.

daemon.err collectd[6465]: exec plugin: Cowardly refusing to exec program as root.
1 Like
fgrep -r 'Cowardly' /usr/
/usr/lib/collectd/exec.so:exec plugin: Cowardly refusing to exec program as root.fgrep -r 'Cowardly' /usr/

cat build_dir/target-arm_cortex-a15+neon-vfpv4_musl_eabi/collectd-5.11.0/src/exec.c | grep -C3 Cowardly
  uid = sp.pw_uid;
  gid = sp.pw_gid;
  if (uid == 0) {
    ERROR("exec plugin: Cowardly refusing to exec program as root.");
1 Like

I would suggest rewriting your monitoring script in a language such as Lua that collectd has a loadable module for. I.e. avoid the exec plugin completely. Then your code will run as a part of collectd process, which runs as root, without any complaints.

@anon50098793: Thank you, so there is no way to run any script from exec as root (uid 0). At least it's a clear answer.

A sound suggestion, but I am really not familiar enough with LUA to pull that off

That's okay. I will go the cronjob route then. Honestly, I think getting the values "asynchronously" is not a bad idea anyway since retrieving values from the modem is rather costly, and doesn't need to happen very frequently.

I am really not familiar enough with LUA

Collectd also has modules for Perl and Python :slight_smile:

I have collectd 5.4.1 running on openwrt 15.05 like this:
1985 root 16348 S /usr/sbin/collectd
1994 nobody 1496 S {coovachilli.sh} /bin/sh /usr/lib/collectd/coovachilli.sh

collectd running as root, started from /etc/init.d "old style". And then execs my custom script. So you might opt for using older version of collectd.

1 Like

CMIIW, but your coovachili.sh is still executed as non-root "nobody"

Yikes.

coovachilli.sh must be explicitly started to run as nobody:

Exec "nobody" "/usr/lib/collectd/coovachilli.sh"

Otherwise its root, too. And does not work.

Could you just copy this into its own script and suid that to root and just call that? I have not tried that, but it might work, no?

Linux does not support setuid scripts. But sudo can be a workable solution.

It's okay, I'm now doing it via a crontab into a temporary file that the exec script then just pipes back to collectd. Thank you for all your suggestions. "There is no (simple/non-workaround-ish) way" is a perfectly valid answer. :slight_smile:

If your problem is solved, please consider marking this topic as [Solved]. See How to mark a topic as [Solved] for a short how-to.

What, no tutorial video? :wink:

1 Like

Quick follow-up question: Is there any public interest in what I concocted up for myself? Then I will upload the files into a Github Gist or something.

Note: "errors" and "errored seconds" are scaled up "per day" to deliver a somewhat informative scale. The collectd "DERIVE" data types normalizes into "per second", which does make sense if you want to display value changes on a time scale. But if you only receive two or three errors in a statistics period, that results into a scale that only displays decimal places which is of no use to anyone.

1 Like

Yes, please! These look like useful monitoring data for marginal lines, It is so much nicer not having to resort to external tools for such monitoring...

Here you go. It is not exceedingly beautiful code, but it isn't bad either and does not contain horrible misterks.

3 Likes

Pointer: With 21.02, the whole process has changed. I rewrote the scripts to collect Lantiq DSL values in collectd for 21.02.