How To Obtain Remote Client SSH IP From Sudo'd Script - bash

I'm trying to create a small setup script which requires the user to enter their own IP address... I'm trying to make the default IP address be that of the connected SSH session.
When I run ${SSH_CLIENT%% *} in the command line, it shows 99.99.99.99: command not found and when I do echo ${SSH_CLIENT%% *} it shows just 99.99.99.99 (where 99.99.99.99 is my actual WAN IP)...
However, the following code (when ran in a bash script) shows
[6/9] Enter your public IP address. (eg. ) :
declare CONNECTED_IP=${SSH_CLIENT%% *}
read -e -p "[6/9] Enter your public IP address. (eg. $CONNECTED_IP) : " ipAddress
[ -z "$ipAddress" ] && ipAddress=$CONNECTED_IP
Update 1
Upon further review, it does work... except for when I run the script with sudo. Could someone explain this behavior to me and is there a work-around?
Update 2
So, it's been explained to be that this behavior is due to the fact that environment variables are not preserved when switching users (sudo) and that I could use the -E flag when executing the script to preserve the environment.
Unfortunately though, this is a script that will be shared and I therefore cannot ensure that the user executes the script with the -E flag. Furthermore, I don't even know if they'll use sudo at all.
That said, I'm looking for a consistent way to obtain the IP address of the user connected via SSH.

The reason the variable doesn't show when using sudo is that it doesn't automatically preserve the environment when you switch from another user. You can use the -E sudo option to preserve the current environment.
$ sudo -E ./script.sh
Should show the ip as expected.
-E, --preserve-env
Indicates to the security policy that the user wishes to preserve
their existing environment variables. The security policy may
return an error if the user does not have permission to preserve
the environment.

Related

How to invoke an interactive bash shell as another user with initial commands?

I currently have a RemoteCommand, RemoteCommand sudo su - admin-user set up in my ssh config that allows me to connect to a system and immediately switch users to an admin user. This is because I must only make changes to the system as that admin-user, the admin-user does not have a password, and I am not allowed to add my public key to the system to connect without specifying a password. This works perfectly; I'm able to login as "login-user", and it immediately switches me to the admin-user.
However, I want to do the same, and create a temporary function that would allow me to run a common command in a more shorthand manner. This is because I am not allowed to change the admin-user's bashrc.
My thought in setting this up was to do the following in my ssh config:
HOST SYSTEM
Hostname 12.34.56.78
User login-user
RemoteCommand sudo -Hu admin-user /usr/bin/bash --init-file <(echo ". ~/bashrc; function testy() { ls ; }") -li
...
In this case, I'm just testing with a function that runs ls. Just using a named pipe to source the normal bashrc and add a func to the new shell via an init file/rc file.
This creates an interactive bash shell as the admin-user as expected, but upon trying to run the function testy in this shell, I get bash: testy: command not found. Doing the same without switching users in the same step works, but not if I add the flags to run the shell as admin-user. I can't figure out how to get this working. Any help using this approach or another is greatly appreciated!
Likely a named pipe sharing issue. You can use another wrapper shell:
HOST SYSTEM
Hostname 12.34.56.78
User login-user
RemoteCommand sudo -Hiu admin-user /usr/bin/bash -c 'exec /usr/bin/bash --init-file <(echo ". ~/bashrc; function testy() { ls ; }")'
...
. ~/.bashrc also likely can be unnecessary but that's besides the main point.

OS X Bash script doesn't work but individual commands do

I'm trying to turn the instructions on this page about connecting to a Soft Ether VPN on OS X into a bash script, but I'm running into some issues.
When I run each of these commands individually at the command line, I'm able to initiate the connection to the VPN just fine and set up the routing appropriately, but when I put it into a script, it doesn't work.
Here is the script in question:
#!/bin/bash
GATEWAY=`route -n get default | grep gateway | awk '{print $2}'`
VPN_IP=130.158.6.123/32
VPN_GATEWAY=192.168.0.1
vpnclient start
vpncmd localhost /CLIENT /CMD AccountConnect HomeVPN;
ipconfig set tap0 DHCP;
ifconfig tap0 down; ifconfig tap0 up
echo "waiting for dhcp to get us an address..."
sleep 15
route delete default;
route -n add $VPN_IP $GATEWAY;
route add default $VPN_GATEWAY;
Upon testing, I have confirmed that GATEWAY gets the correct value and all the other variables are set correctly. The script seems to do everything correctly up until the part where it starts changing the routes. At first I thought it was because the interface hadn't had enough time to get an IP address, so I put a pretty long wait time in to make sure it had an IP before it started trying to change routes.
Any thoughts as to why this doesn't work when put into script form?
Just a guess: sudo doesn't work well in shell scripts as it's an interactive tool and need to prompt for a password. You might consider removing the sudo commands and running the entire script using sudo.

Running interactive Bash commands over ssh

I am trying to automate my server provisioning process using chef. Since I don't want to run chef as root, I need a chef/deployer user. But I don't want to create this user manually. Instead, I want to automate this step. So I took a shot at scripting it but ran into an issue:
The problem is that if I run
>ssh root#123.345.345.567 '/bin/bash -e' < ./add_user.sh
where add_user contains
//..if the username doesnt exist already
adduser $USERNAME --gecos ''
I never see the output or the prompts of the command.
Is there a way to run interactive commands in this way?
Is there a better way to add users in an automated fashion?
Try this:
ssh -t root#<ipaddress> adduser $USERNAME --gecos
Not sure why you have a $ in the IP address in your original example - that's likely to cause ssh to fail to connect, but since you didn't indicate that sort of failure, I'm assuming that's just a typo.
Since add_user.sh is just a simple command, there's no need for the added complexity of explicitly running bash or the redirection, just run the adduser command via ssh.
And lastly, since $USERNAME is likely defined on the local end, and not on the remote end, even if you could get your original command to "do what you said", you'd end up running adduser --gecos on the remote end, which isn't what you intended.
Try using :
ssh -t root#$123.345.345.567 '/bin/bash -e' < ./add_user.sh
instead.

Bash eval inside quotes

I have a bash script which takes one parameter and does something like this:
ssh -t someserver "setenv DISPLAY $1; /usr/bin/someprogram"
How can I force bash to substitute in the $1 instead of passing the literal characters "$1" as the display variable?
Based on your comment on sehe's answer, it sounds like you just want the remote command to use the local X display — so that the program is running on your remote server (someserver) but being displayed on the machine you ran the ssh command on.
This can be done by just passing -X, e.g.
ssh -X someserver /usr/bin/someprogram
For some reason, this doesn't work with a few programs, for example evince. I'm not really sure why. I'm pretty sure that evince is the only app I've had trouble forwarding back over an SSH connection.
If this isn't what you're aiming to do, please explain.
Edit Are you aware of
ssh -X ...
ssh -Y ...
which already support X forwarding out of the box? Also look at
xhost +
in case you need to increase permissions to 'guests'.
If you want to forward non-standard X display address, you could always use
DISPLAY=localhost:3 ssh -XCt user#remote xterm
Bonus: to make ssh background after authentication, add '-f'
What locally? That should already work as shown. Remotely? escape the $: \$
However, I'm not sure where the command would be taking it's arguments ($1) from

How can I ssh directly to a particular directory?

I often have to login to one of several servers and go to one of several directories on those machines. Currently I do something of this sort:
localhost ~]$ ssh somehost
Welcome to somehost!
somehost ~]$ cd /some/directory/somewhere/named/Foo
somehost Foo]$
I have scripts that can determine which host and which directory I need to get into but I cannot figure out a way to do this:
localhost ~]$ go_to_dir Foo
Welcome to somehost!
somehost Foo]$
Is there an easy, clever or any way to do this?
You can do the following:
ssh -t xxx.xxx.xxx.xxx "cd /directory_wanted ; bash --login"
This way, you will get a login shell right on the directory_wanted.
Explanation
-t Force pseudo-terminal allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services.
Multiple -t options force tty allocation, even if ssh has no local tty.
If you don't use -t then no prompt will appear.
If you don't add ; bash then the connection will get closed and return control to your local machine
If you don't add bash --login then it will not use your configs because its not a login shell
You could add
cd /some/directory/somewhere/named/Foo
to your .bashrc file (or .profile or whatever you call it) at the other host. That way, no matter what you do or where you ssh from, whenever you log onto that server, it will cd to the proper directory for you, and all you have to do is use ssh like normal.
Of curse, rogeriopvl's solution works too, but it's a tad bit more verbose, and you have to remember to do it every time (unless you make an alias) so it seems a bit less "fun".
My preferred approach is using the SSH config file (described below), but there are a few possible solutions depending on your usages.
Command Line Arguments
I think the best answer for this approach is christianbundy's reply to the accepted answer:
ssh -t example.com "cd /foo/bar; exec \$SHELL -l"
Using double quotes will allow you to use variables from your local machine, unless they are escaped (as $SHELL is here). Alternatively, you can use single quotes, and all of the variables you use will be the ones from the target machine:
ssh -t example.com 'cd /foo/bar; exec $SHELL -l'
Bash Function
You can simplify the command by wrapping it in a bash function. Let's say you just want to type this:
sshcd example.com /foo/bar
You can make this work by adding this to your ~/.bashrc:
sshcd () { ssh -t "$1" "cd \"$2\"; exec \$SHELL -l"; }
If you are using a variable that exists on the remote machine for the directory, be sure to escape it or put it in single quotes. For example, this will cd to the directory that is stored in the JBOSS_HOME variable on the remote machine:
sshcd example.com \$JBOSS_HOME
SSH Config File
If you'd like to see this behavior all the time for specific (or any) hosts with the normal ssh command without having to use extra command line arguments, you can set the RequestTTY and RemoteCommand options in your ssh config file.
For example, I'd like to type only this command:
ssh qaapps18
but want it to always behave like this command:
ssh -t qaapps18 'cd $JBOSS_HOME; exec $SHELL'
So I added this to my ~/.ssh/config file:
Host *apps*
RequestTTY yes
RemoteCommand cd $JBOSS_HOME; exec $SHELL
Now this rule applies to any host with "apps" in its hostname.
For more information, see http://man7.org/linux/man-pages/man5/ssh_config.5.html
I've created a tool to SSH and CD into a server consecutively – aptly named sshcd. For the example you've given, you'd simply use:
sshcd somehost:/some/directory/somewhere/named/Foo
Let me know if you have any questions or problems!
Based on additions to #rogeriopvl's answer, I suggest the following:
ssh -t xxx.xxx.xxx.xxx "cd /directory_wanted && bash"
Chaining commands by && will make the next command run only when the previous one was successful (as opposed to using ;, which executes commands sequentially). This is particularly useful when needing to cd to a directory performing the command.
Imagine doing the following:
/home/me$ cd /usr/share/teminal; rm -R *
The directory teminal doesn't exist, which causes you to stay in the home directory and remove all the files in there with the following command.
If you use &&:
/home/me$ cd /usr/share/teminal && rm -R *
The command will fail after not finding the directory.
In my very specific case, I just wanted to execute a command in a remote host, inside a specific directory from a Jenkins slave machine:
ssh myuser#mydomain
cd /home/myuser/somedir
./commandThatMustBeRunInside_somedir
exit
But my machine couldn't perform the ssh (it couldn't allocate a pseudo-tty I suppose) and kept me giving the following error:
Pseudo-terminal will not be allocated because stdin is not a terminal
I could get around this issue passing "cd to dir + my command" as a parameter of the ssh command (to not have to allocate a Pseudo-terminal) and by passing the option -T to explicitly tell to the ssh command that I didn't need pseudo-terminal allocation.
ssh -T myuser#mydomain "cd /home/myuser/somedir; ./commandThatMustBeRunInside_somedir"
I use the environment variable CDPATH
going one step further with the -t idea. I keep a set of scripts calling the one below to go to specific places in my frequently visited hosts. I keep them all in ~/bin and keep that directory in my path.
#!/bin/bash
# does ssh session switching to particular directory
# $1, hostname from config file
# $2, directory to move to after login
# can save this as say 'con' then
# make another script calling this one, e.g.
# con myhost repos/i2c
ssh -t $1 "cd $2; exec \$SHELL --login"
My answer may differ from what you really want, but I write here as may be useful for some people. In my solution you have to enter into the directory once and then every new ssh session goes to the same dir (after the first logout).
How to ssh to the same directory you have been in your last login.
(I assume you use bash on the remote node.)
Add this line to your ~/.bash_logout on the remote node(!):
echo $PWD > ~/.bash_lastpwd
and these lines to the ~/.bashrc file (still on the remote node!)
if [ -f ~/.bash_lastpwd ]; then
cd $(cat ~/.bash_lastpwd)
fi
This way you save your current path on every logout and .bashrc put you into that directory after login.
ps: You can tweak it further like using the SSH_CLIENT variable to decide to go into that directory or not, so you can differentiate between local logins and ssh or even between different ssh clients.
Another way of going to directly after logging in is create "Alias". When you login into your system just type that alias and you will be in that directory.
Example : Alias = myfolder '/var/www/Folder'
After you log in to your system type that alias (this works from any part of the system)
this command if not in bashrc will work for current session. So you can also add this alias to bashrc to use that in future
$ myfolder => takes you to that folder
I know this has been answered ages ago but I found the question while trying to incorporate an ssh login in a bash script and once logged in run a few commands and log back out and continue with the bash script. The simplest way I found which hasnt been mentioned elsewhere because it is so trivial is to do this.
#!/bin/bash
sshpass -p "password" ssh user#server 'cd /path/to/dir;somecommand;someothercommand;exit;'
Connect With User
In case if you don't know this, you can use this to connect by specifying both user and host
ssh -t <user>#<Host domain / IP> "cd /path/to/directory; bash --login"
Example: ssh -t admin#test.com "cd public_html; bash --login"
You can also append the commands to be executed on every login by appending it in the double quotes with a ; before each command
Unfortunately, the suggested solution (of #rogeriopvl) doesn't work when you use multiple hops, so I found another one.
On remote machine add into ~/.bashrc the following:
[ "x$CDTO" != "x" ] && cd $CDTO
This allows you to specify the desired target directory on command line in this way:
ssh -t host1 ssh -t host2 "CDTO=/desired_directory exec bash --login"
Sure, this way can be used for a single hop too.
This solution can be combined with the usefull tip of #redseven for greater flexibilty (if no $CDTO, go to saved directory, if exists).
SSH itself provides a means of communication, it does not know anything about directories. Since you can specify which remote command to execute (this is - by default - your shell), I'd start there.
simply modify your home with the command:
usermod -d /newhome username

Resources