exec without a shell

92 views
Skip to first unread message

Stefan Schulte

unread,
Jan 26, 2016, 8:40:06 PM1/26/16
to Puppet Users
Hello,

I've got a quick question about the exec type. The exec type does have a
shell provider and a posix provider and the posix provider says

#
https://github.com/puppetlabs/puppet/blob/master/lib/puppet/provider/exec/posix.rb
Executes external binaries **directly, without passing through a
shell** or
performing any interpolation. This is a safer and more predictable way
to execute most commands, but prevents the use of globbing and shell
built-ins (including control logic like "for" and "if" statements).

but when I test the following manifest:

$unsafe_input = "I will fail; /bin/false"

exec { 'Test01':
command => "/bin/echo ${unsafe_input}",
provider => posix,
}

exec { 'Test02':
command => shellquote("/bin/echo", $unsafe_input),
provider => posix,
}


then the first test will fail (because /bin/false is executed instead of
printed), while the second test does work (I am not sure how reliable
shellquote acutally works though).

# on puppet version 4.3.1:
Notice: /Stage[main]/Main/Exec[Test01]/returns: I will fail
Error: /bin/echo I will fail; /bin/false returned 1 instead of one
of [0]
Error: /Stage[main]/Main/Exec[Test01]/returns: change from notrun to
0 failed: /bin/echo I will fail; /bin/false returned 1 instead of one of [0]
Notice: /Stage[main]/Main/Exec[Test02]/returns: executed successfully

Am I misreading the documentation here?

- Stefan

jcbollinger

unread,
Jan 27, 2016, 9:28:43 AM1/27/16
to Puppet Users
No, I think rather that the documentation is misleading, and / or this is a longstanding Puppet bug.  I recently had occasion to look at the Ruby implementation of Exec and its standard providers.  Puppet implements the "posix" provider by passing the command to Ruby's Kernel.exec().  Until I read its docs (linked), I had supposed that Kernel.exec() was a more-or-less direct interface to the POSIX execve() family of functions, which indeed would do as the Puppet docs describe.

In fact, the behavior of Kernel.exec() depends on the form of its arguments.  Some forms do seem to map directly onto calls to the C exec-family functions, but Puppet uses the one-arg form, which does not.  I will not rehash the details from the linked docs, but the bottom line for your particular example is that including a semicolon (;) in your command string ensures that it will be run via a shell.

Since the behavior is at odds with the docs, I guess there is necessarily either a documentation flaw or an implementation flaw here.


John


Stefan Schulte

unread,
Jan 27, 2016, 7:48:20 PM1/27/16
to puppet...@googlegroups.com


On 27.01.2016 15:28, jcbollinger wrote:
[...]

Puppet implements the "posix" provider by passing the command to Ruby's Kernel.exec().
[...]

Thanks for the inside view John. This would explain the current behaviour. IIRC this behaves quite similar to perl's "system" command (single argument vs. array). I also just remembered about some old discussions on redmine but I could only dig up

http://projects.puppetlabs.com/issues/4288#note-16

which all talk about feeding the exec with the "array form" but that does not work as expected either.

    # test.pp
    exec { 'Test03':
      command  => [ "/bin/echo", "arg1", "arg2", "arg3" ],
      provider => posix,
    }
    $ puppet apply test.pp
    Error: Parameter command failed on Exec[Test03]: Command must be a String,
    got value of class Array at /home/stefan/test.pp:1

So without the help of the shellescape function (which I strangely never saw in the wild before) I basically give everyone with access to hiera data root-rights as a bonus (If I happen to use hiera data to generate command line arguments).

I guess the sane approach would be to add a feature request to allow passing an array as a command parameter which in turn would run ruby's exec with an array as well, bypassing the shell.

-- Stefan

Stefan Schulte

unread,
Jan 27, 2016, 7:55:30 PM1/27/16
to puppet...@googlegroups.com


On 28.01.2016 01:46, 'Stefan Schulte' via Puppet Users wrote:
[...]

I guess the sane approach would be to add a feature request to allow passing an array as a command parameter which in turn would run ruby's exec with an array as well, bypassing the shell.
[...]
-- Stefan

just for reference. There already is such a feature request: https://tickets.puppetlabs.com/browse/PUP-3142
Reply all
Reply to author
Forward
0 new messages