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

Using Linux and remote ssh command (I've placed my public ssh key on the remote server to allow non-password prompted connects) The following command runs fine from the Linux command line:
ssh root@remoteIP /usr/bin/ls
However, when the following is called from my perl script:
system("ssh root@remoteIP /usr/bin/ls");
Perl reports:
ssh: Could not resolve hostname root.remoteIP: Name or service not kno +wn remoteIP is the real IP address of the remote computer.
Obviously, I need to tell Perl how to resolve the address?

Original content restored above by GrandFather

Using Linux and remote ssh command (I've placed my public ssh key on the remote server to allow non-password prompted connects)

The following command runs fine from the Linux command line:

ssh root@remoteIP /usr/bin/ls

However, when the following is called from my perl script:

system("ssh root\@\remoteIP /usr/bin/ls");

I get prompted for the remote server's password when I run the perl script. I shouldn't be

Replies are listed 'Best First'.
Re: Calling a remote ssh connection from perl (updated)
by haukex (Archbishop) on May 27, 2021 at 22:39 UTC
Re: Calling a remote ssh connection from perl
by hippo (Archbishop) on May 28, 2021 at 10:07 UTC
    system("ssh root\@\remoteIP /usr/bin/ls");

    The double quotes interpolate so that \r is going to become a carriage return. That's one source of problems.

    Some other tips:

    • Don't test as root. Use an unprivileged user so you don't destroy the remote host
    • Pre-construct your command so you can view it. eg:
      my $cmd = "ssh $user\@$host /usr/bin/ls"; warn "Executing '$cmd'"; system ($cmd);
    • Consider passing a list to system to avoid some shell processing complications
    • Don't roll your own but instead use a module such as Rex

    🦛

Re: Calling a remote ssh connection from perl
by bliako (Abbot) on May 28, 2021 at 05:38 UTC

    Perl's system() may or may not invoke a shell. If it does, then it may not be the usual shell you get when you login (e.g. bash, sh, csh), or it may be that, but it may not be interactive and so it may be missing some crucial initialisation you otherwise get when running ssh at the command line.

    If it doesn't call a shell, it will do an execvp which inherits the environment BUT it will not do the initialisations, conditional initialisations, it will not call other scripts etc. like one gets when opening a prompt and a new interactive shell.

    If it does call a shell, it may not be the one you are normally using (sh vs bash) and so it will be doing initialisations which are not specific to your setting. Even if you explicitly specify which shell to use, it will not be "interactive" unless you specify so explicitly as well. That's important because many shell init scripts distinguish between interactive and non-interactive invocations. This further complicates things because a lot of functionality in these init scripts is conditional on interactivity.

    If there is only one scalar argument, the argument is checked for shell metacharacters, and if there are any, the entire argument is passed to the system's command shell for parsing (this is "/bin/sh -c" on Unix platforms, but varies on other platforms). If there are no shell metacharacters in the argument, it is split into words and passed directly to "execvp", which is more efficient.

    The bottomline is: read the manual and then choose the execution mode which exactly matches your requirement and be explicit on what you want, do not rely on Perl's defaults.

    This will run your ssh command in an interactive (-i), bash shell, assuming this is the one you get when you login and open a prompt: system('/bin/bash', '-i', '-c', 'ssh root@remoteIP /usr/bin/ls'); . Whether that will do what you want is doubtful as there are other factors in your environment which I am not aware of.

    bw, bliako

Re: Calling a remote ssh connection from perl
by LanX (Saint) on May 27, 2021 at 22:34 UTC
    > ssh: Could not resolve hostname root.remoteIP: Name or service not known

    are you sure you are showing us the right code and error message?

    This

    system("ssh root@remoteIP /usr/bin/ls");

    will try to interpolate an array @remoteIP into the double-quoted string

    so better either write

    system("ssh root\@remoteIP /usr/bin/ls")

    or

    system('ssh root@remoteIP /usr/bin/ls')

    BTW: did you use strict and warnings ?

    HTH! :)

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      Yes, I forgot to escape from things. So I modified the command
      system("ssh root\@remoteIP /usr/bin/ls");

      and that seems to connect to the remote server but now it prompts for the password. And when I enter the password, I get a Permission Denied error.

        see haukex' suggestion to use Net::OpenSSH, I second this.

        And you've changed the original post I replied to, this makes reading and understanding the thread very hard for others.

        (it was system("ssh root@remoteIP /usr/bin/ls"); )

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Re: Calling a remote ssh connection from perl
by Perlbotics (Archbishop) on May 30, 2021 at 18:01 UTC

    A side node on security: It's not a good idea to allow root access from the internet or even a local network. Consider to create a non-privileged management-user account and then issue specific sudo /bin/whatever commands (w/o password) by that particular user.
    After testing, disable root access (i.e. /etc/ssh/sshd_config). Take care to disable password expiration for that user and change PKs regularily.
    That are the basics. Study the hardening documentation of your distro for more measures.
    Yes, it is tedious and more work.

      A side node on security: It's not a good idea to allow root access from the internet or even a local network. Consider to create a non-privileged management-user account and then issue specific sudo /bin/whatever commands (w/o password) by that particular user. After testing, disable root access (i.e. /etc/ssh/sshd_config). Take care to disable password expiration for that user and change PKs regularily. That are the basics. Study the hardening documentation of your distro for more measures.

      All good and right. But OpenSSH has another nice feature that we use for our backups:

      There is a central Linux backup server that connects to each real and virtual Linux machine in need of a backup. It does so as root, both on the backup and on the target machine, on a dedicated network, using a public key distributed to all target machines. After connecting, it runs rsync through the ssh connection to fetch changes since the last backup. All target machines are essentially configured the same. root can login, but only using public keys stored in /root/.ssh/authorized_keys. The relevant line in /etc/ssh/sshd_config is PermitRootLogin prohibit-password. Up to this point, the root user on the backup server would be allowed to run any command as root on the target machine. But the authorized_keys file can do more than that: You can specify a command (using the command option) that will be executed INSTEAD of the command passed via the ssh connection. In our case, it's a simple shell script that aborts for anything but rsync, by checking the original command passed via $ENV{'SSH_ORIGINAL_COMMAND'}. Effectively, this limits the root access via ssh to running rsync. Also in the authorized_keys file, the access is restricted to only the backup server, and only via the dedicated backup network, using the from option.

      Combining this features, you get a very restricted root access, not needing any passwords, but still reasonable secure. Of course, this trick also works for other users, and allows even more restricted access. Combine that with a restrictive sudoers file, and you should be pretty secure.

      Yes, one could abuse rsync to write files TO the target machine instead of reading changes FROM the target machine. Using a smarter command that knows more about rsync parameters could prevent that. But to do so, you would have to gain root access to our backup server, and at that moment, we would be screwed anyway.

      Just for reference, this is our authorized_keys file:

      from="the.one.backup.server",command="${HOME}/.ssh/ssh_validate.sh",no +-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa + AA...Rw== some-note-about-this-key

      It's not perfect. Instead of selectively disabling features, all features should be disabled using the restrict option, as OpenSSH promises to add any future restriction capabilities to this option. And the command path should be absolute, not relying on $ENV{'HOME'} to be set correctly.

      And this is ssh_validate.sh:

      #!/bin/sh case "$SSH_ORIGINAL_COMMAND" in *\&*) echo "Rejected" ;; *\;*) echo "Rejected" ;; rsync*) $SSH_ORIGINAL_COMMAND ;; *) echo "Rejected" ;; esac

      Again, it's not perfect. It prevents running stuff in background, or several commands combined using semicolons, and anything not starting with rsync. I can think of a few ways to do evil stuff despite these limitations. $ENV{'SSH_ORIGINAL_COMMAND'} should be validated much more. Maybe one day, I'll find some time to fix that.

      I did not invent that mechanism, I inherited it from the previous root, and it is sufficient for our company. Only three people can legally gain root access, and from the other ones not more than two might have or gain the knowledge required to illegally gain root access and to bypass ssh_validate.sh.

      But to cause as much damage, no knowledge of Linux is required. Just find a big hammer from the workshop, and hit anything roughly looking like a server as hard as you can until it stops emitting sparks or any sounds. Almost anyone in the company knows where the main servers are mounted, and most of them also know the location of the backup server. That attack is probably also much faster than fiddling with the Linux command line. Yes, there are doors with locks between the servers and the outside world, but nothing that would withstand a big hammer.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: Calling a remote ssh connection from perl
by Anonymous Monk on May 28, 2021 at 20:48 UTC
    Also – is ssh-agent running?