Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Automating sudo actions

by rastoboy (Monk)
on Jan 25, 2011 at 02:07 UTC ( [id://884031]=perlquestion: print w/replies, xml ) Need Help??

rastoboy has asked for the wisdom of the Perl Monks concerning the following question:

Hello my brothers, I find myself in the unexpectedly difficult position of automating tasks that must be executed via sudo on Linux. Obviously, when executing a system command with sudo, you will be prompted for a password. However, I am also unable to install perl modules--such as Expect. And I find Expect to be very clunky and unreliable in any case, unless you know exactly what to Expect every time :-)

Does anyone know any tricks for manipulating a bash session via Perl without Expect?

Replies are listed 'Best First'.
Re: Automating sudo actions
by merlyn (Sage) on Jan 25, 2011 at 02:35 UTC
    If you're the root on the box, set up passwordless sudo, at least for the activities of interest. Passwordless sudo will be more secure than burying your password in an Expect script, by far.

    -- Randal L. Schwartz, Perl hacker

    The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

      Thanks for that. However, the problem is beyond the scope of merely passing the sudo password, but actually doing stuff after that. How do I "do stuff" while still maintaining the terminal? If I just execute the sudo command from a system or backticks call in Perl, that terminal vanishes instantly afterward, and my next system command executes in a new terminal, losing the sudo priviledges.
        If you want to run multiple commands in a batch, there's this thing called a script... sudo that!

        How is that different than using sudo from the command line? In my (limited) experience with *nix sudo is required to prefix each command that requires privilege. You can of course capture the output from the commands you run should you need to further process or log the results. That in addition to setting up passwordless sudo permissions ought allow you to do all you've described so far.

        True laziness is hard work
Re: Automating sudo actions
by roboticus (Chancellor) on Jan 25, 2011 at 11:13 UTC

    rastoboy:

    If you're going to schedule the task, you can avoid sudo altogether (at least for running the task) by putting it on the roots crontab. Then you'll only need sudo for editing the crontab.

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

Re: Automating sudo actions
by zentara (Archbishop) on Jan 25, 2011 at 17:01 UTC
    Does anyone know any tricks for manipulating a bash session via Perl without Expect?

    Here is a commandline and a Tk front end to a bash shell interpreter. A select loop would replace the Tk event loop's fileevent in the cmd version.

    #!/usr/bin/perl use warnings; use strict; use IPC::Open3; use IO::Select; my $pid = open3(\*WRITE, \*READ,\*ERROR,"/bin/bash"); my $sel = new IO::Select(); $sel->add(\*READ); $sel->add(\*ERROR); my($error,$answer)=('',''); while(1){ print "Enter command\n"; chomp(my $query = <STDIN>); #send query to bash print WRITE "$query\n"; foreach my $h ($sel->can_read) { my $buf = ''; if ($h eq \*ERROR) { sysread(ERROR,$buf,4096); if($buf){print "ERROR-> $buf\n"} } else { sysread(READ,$buf,4096); if($buf){print "$query = $buf\n"} } } } waitpid($pid, 1); # It is important to waitpid on your child process, # otherwise zombies could be created.

    And the Tk version:

    #!/usr/bin/perl use warnings; use strict; use Tk; use IPC::Open3; require Tk::ROText; $|=1; my $mw = new MainWindow; my $entry=$mw->Entry(-width => 80)->pack; $mw->Button(-text => 'Execute', -command => \&send_to_shell)->pack; my $textwin =$mw->Scrolled('ROText', -width => 80, -bg =>'white', -height => 24, )->pack; $textwin->tagConfigure( 'err', -foreground => 'red' ); my $pid = open3( \*IN, \*OUT, \*ERR, '/bin/bash' ) or warn "$!\n"; $mw->fileevent( \*OUT, readable => \&read_stdout ); $mw->fileevent( \*ERR, readable => \&read_stderr ); $entry->bind('<Return>',[\&send_to_shell]); $entry->focus; MainLoop; sub read_stdout { if( sysread( OUT, my $buffer, 1024 ) > 0 ){ $textwin->insert( 'end', $buffer ); $textwin->see('end'); } } sub read_stderr { if( sysread(ERR, my $buffer, 1024 ) > 0 ){ $textwin->insert( 'end', $buffer, 'err' ); $textwin->see('end'); } } sub send_to_shell { my $cmd= $entry->get(); print IN "$cmd\n"; }

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
      That is totally awesome zentara, thanks so much!

      I never would have guessed that IO::Select was necessary--can you explain why it is? I've had lots of weirdness using IPC::Open3 in the past and I suspect there's something unixy I don't understand properly.

      I always thought that IPC::Open3 was designed specifically so that you could just talk and listen to a process you spawned, but it doesn't seem to work that way (for long) in practice.

        The reason you need select or else IO::Select is very simple. When you are both talking to and listening from a process it is very important that you are not talking when it is trying to talk, or listening when it is trying to listen. If that happens you'll get a deadlock which lasts forever.

        What select (either version) does is tell you whether it is ready to talk or receive more input, so that you can correctly send or receive data. That way you know which you can safely do and avoid deadlocks.

        Please note that doing this correctly is complicated. If at all possible you want to arrange to need to talk to an external process, or listen from it, but not both.

        In addition to what tilly said about select, in this case there is an additional, and often useful technique to actually SEPARATE the stdout and stderr stream. In IPC::Open3, if you set the \*ERR handle to 0, it will go to stdout, like in IPC::Open2. But what if you wanted your stderr to go somewhere different, or be highlighted in different colors, or go to a log file?. Then the general purpose techique shown above can be used. It is almost the way you would do it in c. Most people forget about select, because many modules handle those details transparently for them.

        But it is a cute code fragment, isn't it? You know, us old geeks, drool over elegant time-tested code fragments. :-)


        I'm not really a human, but I play one on earth.
        Old Perl Programmer Haiku ................... flash japh
Re: Automating sudo actions
by chrestomanci (Priest) on Jan 25, 2011 at 11:53 UTC

    Another approach would be to make an ssh connection to root@localhost using an ssh key that is authorised for the purpose, and limited to only do the required things.

    In the /root/.ssh/authorised_keys file, you can put a lot of fancy configuration against they key to only allow certain commands to be run, and only accept connections from certain machines.

    On the command issuing side, the ssh key need not have a pass phrase, because what it can be used for as root will be very strictly limited. It will be impossible to use it to get a shell for example.

      I would add a caveat (absolutes in security, and other areas of life, are asking to be shown the exception).

      It will be impossible to use it to get a shell for example.

      If properly written to avoid exploitation, it will be impossible to use it to get a shell for example.

      --MidLifeXis

        Thanks for all the great suggestions. Unfortunately, changing the environment is quite impossible. I'm stuck having to 'sudo bash'. If I can't automate through that (icky) interface, I'm stuck largely copy/pasting chunks of commands.

        Can this be done in perl?

Re: Automating sudo actions
by kirillm (Friar) on Jan 26, 2011 at 08:48 UTC

    EDIT: Re-reading the thread, I see that this is not what you're looking for.

    If you're admin on that box, then you can avoid password prompt by using NOPASSWD in the sudoers file. Like this:

    # User privilege specification root ALL=(ALL) ALL ftpauth ALL=(ftpdata) NOPASSWD: /bin/mkdir
    Which will allow the user ftpauth to run mkdir as user ftpdata without asking for password.

Re: Automating sudo actions
by zengargoyle (Deacon) on Jan 26, 2011 at 16:02 UTC
    try this:
    #!/usr/bin/perl # /home/zengargoyle/sudotest.pl exec '/usr/bin/sudo', '/bin/bash', '-c', $0 unless exists $ENV{SUDO_US +ER}; # if not angsty sysadmins vs sudo commands use this. # exec '/usr/bin/sudo', $0 unless exists $ENV{SUDO_USER}; print "hello from root\n"; system 'ls';
    chmod +x it.
    $ cd / $ /home/zengargoyle/sudotest.pl [sudo] password for zengargoyle: hello from root auto dev initrd.img.old mnt ... ... $
      Thanks tilly and zentara for the great explanations. I'm feeling a little more educated now :-)

      Thanks for the other tricks from the other folks as well!

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://884031]
Approved by ww
Front-paged by MidLifeXis
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others learning in the Monastery: (4)
As of 2024-04-24 12:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found