sudo'd mysqldump over ssh syntax? - bash

I'd like a single command that:
ssh's into my server as user foo, using the public keys I have set up
executes a mysqldump of some database with the /etc/mysql/debian.cnf defaults-file
to stdout, so I can pipe it locally
while doing a sudo on the server remotely, because user foo is not allowed to read /etc/mysql/debian.cnf. foo is allowed to sudo bash but not sudo mysqldump.
This is the best I have come up:
echo 'mysqldump --defaults-file=/etc/mysql/debian.cnf dbname' | ssh -t -i keys/id_rsa -l foo example.com sudo bash -s
This ugly beast works, but produces: Pseudo-terminal will not be allocated because stdin is not a terminal., and I really don't like the echo. There must be a better way?

ssh -i keys/id_rsa foo#example.com sudo bash -c "'mysqldump --defaults-file=/etc/mysql/debian.cnf dbname'"
This will only work if sudo doesn't need to ask for a password. If it does, you need the -t option to ssh.
Note the double and single quotes. The outer quotes will get taken away by your local shell, and the whole 'mysqldump --defaults-file=/etc/mysql/debian.cnf dbname' string will be passed to ssh as a single argument. Ssh will pass that to the remote sudo, so your remote will see the single quotes. The remote bash needs the single quotes to interpret the part after -c as a single argument.

Related

How to sudo su; then run command

Can anyone help me to to solve following issue
i need to ssh to another server by e.g. ubuntu user which has permission to run sudo su fore sure then execute pm2 restart command
full command look like this
#!/bin/sh
CMD="sudo su; pm2 restart 0; pm2 restart 1; exit;"
ssh -i somepemfile.pem ubuntu#1.1.1.1 $CMD
for example i can run normally any command with sudo
CMD="sudo /etc/init.d/apache2 restart"
but with sudo su case it somehow hang and do not response
Unless you have an unusual setup, you can't normally string su with other preceding commands like that. I would imagine it is running sudo su, then hanging in the root environment/session, because it's waiting for you to exit before preceding to the pm2 commands. Instead, I would consider something along the lines of this using the -c option:
CMD="sudo su -c 'pm2 restart 0; pm2 restart 1'"
ssh -i somepemfile.pem ubuntu#1.1.1.1 "$CMD"
As suggested in another answer, it would also probably be useful to encapsulate the $CMD variable in quotes in the ssh call.
su normally puts you in a sub shell which you can see by echoing the current PID (process id)
$ echo $$
94260
$ sudo echo $$
94260
$ sudo su
$ echo $$
94271
But to get around this you can pipe the commands you want to run to su like this
$ echo "whoami" | sudo su
root
And we run multiple commands
$ echo "uptime;whoami" | sudo su
11:29 up 8 days, 19:20, 4 users, load averages: 4.55 2.96 2.65
root
Now to make this work with ssh
$ ssh wderezin#localhost 'echo "uptime;whoami" | sudo su'
sudo: no tty present and no askpass program specified
Darn it, we need allocate a tty for the su command. Add the -t option which allocates a TTY during the remote execution.
$ ssh -t wderezin#localhost 'echo "uptime;whoami" | sudo su'
11:36 up 8 days, 19:26, 5 users, load averages: 2.97 2.97 2.76
root
Your command would look this
ssh -i somepemfile.pem ubuntu#1.1.1.1 'echo "pm2 restart 0; pm2 restart1" | sudo su'
Use -c option of su to specify the command
From man su
In particular, an argument of -c will cause the next argument to be treated as a command by most command interpreters. The command will be executed by the shell specified in
/etc/passwd for the target user.
CMD="sudo su -c \"pm2 restart 0; pm2 restart 1;\""
You need to quote the expansion so that the entire string is parsed on the remote end.
ssh -i somepemfile.pem ubuntu#1.1.1.1 "$CMD"
Otherwise, the expansion is subject to word splitting, and the remote shell gets a string which consists of the command sudo and the arguments su;, restart, 0;, pm2, restart;, 1;, and exit;. That is, ssh will escape the semicolons when it builds a single string from the separate arguments you pass.
However, that doesn't solve the problem of running pm2 in the shell started by sudo. That is addressed by ramki.

Redirecting an output of command executed using Plink on double-hop SSH session

From my Windows system, I'm connecting via SSH to a remote system [remote1], and then connecting to another remote system [remote2] which remote1 has connectivity to, but my Windows system doesn't.
Here's an example that is working;
plink -ssh -pw password -batch root#remote1 ssh remote2 "sed -i 's/param=.*/param=newValue/' /root/test.txt"
This routine connects to remote1 via Plink, then connects to remote2 via ssh, then checks for a string param= and if it exists, replaces it with param=newValue. Again, this is working.
Here's what isn't working;
plink -ssh -pw password -batch root#remote1 ssh remote2 "grep -q -F 'param=newValue' /root/test.txt || echo 'export param=newValue' >> /root/test.txt"
This routine connects in the same way to remote1 and remote2, and then searches for param=newValue and if it doesn't exist, appends param=newValue to the end of the file. When I run this on Windows command line, it takes a couple seconds then exits with no errors, but the test.txt file is unchanged.
If I remote into remote1 using putty and then run the same command starting from ssh remote2 "grep ... then it does append the test.txt file.
I've tried escaping both | and >, but neither worked.
I've determined that the second half of the command is the part that is failing.
echo 'export param=newValue' >> /root/test.txt
More specifically, it appears to be the redirect portion, as I'm able to echo to the console when I remove the redirect.
It's say that the double-quotes are lost at some early stage (when executing Plink already), making the redirection happen on the first server already.
Consider providing the command to Plink using a different method.
either using -m switch (recommended):
plink -ssh -pw password -batch root#remote1 ssh remote2 -m command.txt
With command.txt containing
grep -q -F 'param=newValue' /root/test.txt || echo 'export param=newValue' >> /root/test.txt
or using an input redirection:
plink -ssh -pw password -batch -T root#remote1 ssh remote2 < command.txt
Note the -T switch, that ensures that a shell is started in non-interactive mode - the same mode as used when the command is specified on command-line (like you original wanted) or using -m switch (as above).
Normally, when you provide a command using an input redirection, an interactive shell session is started.
Even with -T switch, the command is still executed using a "shell" SSH channel, contrary to "exec" SSH channel, when providing the command on command-line or using -m switch. So you experience some differences.
Or you can store the command on either of the servers to a shell script.

Remote login (ssh differences)

I would like to know what is the difference between the below commands:
ssh vagrant#someipaddress
cd /home/vagrant/
grep -i "something" data.txt
and
ssh vagrant#someipaddress 'cd /home/vagrant; cat data.txt' | grep -i "something"
From this website it mentions, that you can send multiple commands to the remote server. Is the second option actually logging into the server? What is the benefit in this second approach?
Strictly Speaking from the example provided:
The first command:
Logs onto the remote server
Executes a couple commands, and
Stays logged on to the server
The second command runs half on the remote machine, logs out of the remote machine, and then pipes the output to grep on your local machine, all in one command line.
Breaking down what's happening:
ssh vagrant#someipaddress 'cd /home/vagrant; cat data.txt' | grep -i "something"
The section in bold is running on your local PC, based on the output from the ssh session
The 'quotes "contain" the entire command block
the " quotes "contain" the individual arguments within the command block.
You may have meant to do this:
ssh vagrant#someipaddress 'cd /home/vagrant; cat data.txt' | grep -i "something"
Where the bold section runs locally
Or you may have intentionally done this:
ssh vagrant#someipaddress 'cd /home/vagrant/ | grep -i "something" data.txt'
Where the entire command runs on the server.
Either way, the end result:
Is that you automatically log out of the remote machine, and the whole command sequence was executed in one hit.

Capture output of double-ssh (ssh twice) session as BASH variable

I'd like to capture the output of an ssh session. However, I first need to ssh twice (from my local computer to the remote portal to the remote server), then run a command and capture the output.
Doing this line-by-line, I would do:
ssh name#remote.portal.com
ssh remote.server.com
remote.command.sh
I have tried the following:
server=remote.server.com ##define in the script, since it varies
sshoutput=$(ssh -tt name#remote.portal.com exec "ssh -tt ${server} echo \"test\"")
echo $sshoutput
I would expect the above script to echo "test" after the final command. However, the outer ssh prompt just hangs after I enter my command and, once I Ctrl+c or fail to enter my password, the inner ssh session fails (I believe since stdout is no longer printed to screen and I no longer get my password prompt).
If I run just the inner command (i.e., without "sshoutput=$(" to save it as a variable), then it works but (obviously) does not capture output. I have also tried without the "exec".
I have also tried saving the inner ssh as a variable like
sshoutput=$(ssh -tt name#portal myvar=$(ssh -tt ${server} echo \"test\"") && echo $myvar)
but that fails because BASH tries to execute the inner ssh before sending it to the outer ssh session (I believe), and the server name is not recognized.
(I have looked at https://unix.stackexchange.com/questions/89428/ssh-twice-in-bash-alias-function but they simply say "more flags required if using interactive passwords" and do not address capturing output)
Thanks in advance for any assistance!
The best-practice approach here is to have ssh itself do the work of jumping through your bouncehost.
result=$(ssh \
-o 'ProxyCommand=ssh name#remote.portal.com nc -w 120 %h %p' \
name#remote.server.com \
"remote.command.sh")
You can automate that in your ~/.ssh/config, like so:
Host remote.server.com
ProxyCommand ssh name#remote.portal.com nc -w 120 %h %p
...after which any ssh remote.server.com command will automatically jump through remote.portal.com. (Change nc to netcat or similar, as appropriate for tools that are installed on the bouncehost).
That said, if you really want to do it yourself, you can:
printf -v inner_cmd '%q ' "remote.command.sh"
printf -v outer_cmd '%q ' ssh name#remote.server.com "$inner_cmd"
ssh name#remote.portal.com bash -s <<EOF
$outer_cmd
EOF
...the last piece of which can be run in a command substitution like so:
result=$(ssh name#remote.portal.com bash -s <<EOF
$outer_cmd
EOF
)

How to copy echo 'x' to file during an ssh connection

I have a script which starts an ssh-connection.
so the variable $ssh start the ssh connection.
so $SSH hostname gives the hostname of the host where I ssh to.
Now I try to echo something and copy the output of the echo to a file.
SSH="ssh -tt -i key.pem user#ec2-instance"
When I perform a manual ssh to the host and perform:
sudo sh -c "echo 'DEVS=/dev/xvdbb' >> /etc/sysconfig/docker-storage-setup"
it works.
But when I perform
${SSH} sudo sh -c "echo 'DEVS=/dev/xvdb' > /etc/sysconfig/docker-storage-setup"
it does not seem to work.
EDIT:
Also using tee is working fine after performing an ssh manually but does not seem to work after the ssh in the script.sh
The echo command after an ssh of the script is happening on my real host (from where I'm running the script, not the host where I'm performing an ssh to). So the file on my real host is being changed and not the file on my host where I've performed an ssh to.
The command passed to ssh will be executed by the remote shell, so you need to add one level of quoting:
${SSH} "sudo sh -c \"echo 'DEVS=/dev/xvdb' > /etc/sysconfig/docker-storage-setup\""
The only thing you really need on the server is the writing though, so if you don't have password prompts and such you can get rid of some of this nesting:
echo 'DEVS=/dev/xvdb' | $SSH 'sudo tee /etc/sysconfig/docker-storage-setup'

Resources