Can someone explain to me what stty does? - bash

I have a shell script inside Docker. This is not mine, unfortunately I get the following error when running it. But only under Ubuntu 20.04 and Docker 19.03.
stty: 'standard input': Inappropriate ioctl for device
Here a cutout of line 126 from the script. What makes the stty echo and stty -echo? And why does the script run without problems on an system without docker?
askNoEcho() {
PROMPT=$1
DEFAULT=$2
stty -echo
ask "$PROMPT" "$DEFAULT"
stty echo
echo ""
}
askNonBlankNoEcho() {
PROMPT=$1
DEFAULT=$2
while true; do
stty -echo
ask "$PROMPT" "$DEFAULT"
stty echo
echo ""
if [[ -n "$response" ]]; then
break
fi
echo "A non-blank answer is required"
done
}

stty sets the terminal modes on the terminal connected to stdin. If stdin is not a terminal (eg, it's been redirected from a file when running your script), then it will display the error you show
stty -echo turns off echoing of input keystrokes. Normally when you type in a terminal, the characters you type are echoed back so you can see what you typed. stty echo turns echoing back on. The net effect is to disable echoing for the line entered in response to the prompt -- so it will print a prompt and wait for input, (and return the input), but that input will not be visible on the screen. This is commonly done for entering a password or passphrase.
There are many other terminal modes and settings that can be changed or queried with stty with various arguments. The manual page (man stty) will give you lots of additional info.

Try
$ stty echo < ~/tmp/some.file
stty: standard input: Inappropriate ioctl for device
so, stty expects its standard input to be a terminal.
Therefore, if you run on your terminal
$ stty echo
you will receive no errors.

Related

How to prevent text from being echoed over netcat (nc) when reading it with bash's read -s?

I have an authentication implemented in a bash script.
A user enters his password through this line of code:
read -s password
When entering the password over nc the password gets echoed like the -s option would not have been used.
This doesn't happen when running the script normally.
Is there an alternative to nc or an option for it that supports the -s option of bash's read function?
I tried this:
stty -echo
read -s password
stty echo
but the password still gets displayed.

trap "stty echo" INT has no effect when used with read -s

Given this bash script:
stty -echo
echo $(stty)
reset() {
stty echo
echo $(stty)
exit
}
trap reset int
read -s
sleep 10
I expected the echo option to be enabled, but after pressing ctrlc it is still disabled, even though I have ran stty echo (as you can see in the output by the reset function).
As #KamilCuk has noticed in the comments, read saves the configuration and restores it when the process exists. That results in modifications done while read was running being discarded. The solution is to restore the defaults before running read and redoing them after read finishes.
stty -echo
echo $(stty)
reset() {
stty echo
echo $(stty)
exit
}
trap reset int
stty echo # This line has been added
read -s
echo read finished
stty -echo # This line has been added
sleep 10
#JonathanLeffler also noted that
It can be useful to use old=$(stty -g) to capture the current terminal settings, and then use stty "$old" to reinstate those settings.
Explaination:
Using it allows you to reinstate the exact terminal settings when stty -g was invoked. This can be more reliable than undoing changes with stty -echo etc.
Which I think is the more proper solution, as the stty may run in no-echo mode by default.

Automatize the cert creation OpenVPN

I do not know why I am getting an error when I run my script with SSH, but when I run the bash from my CA server everything works fine.
I installed my VPN server based on this article https://www.digitalocean.com/community/tutorials/how-to-set-up-an-openvpn-server-on-ubuntu-18-04
I wrote a bash for the VPN creation but when I try to run it I need to SSH to the other server at some point. If I start the script with SSH in it I got an error message:
>./easyrsa: 341: set: Illegal option -o echo
My bash contain this and run from my VPN server:
sshpass -p $PASSWORD ssh username#"CA server IP" "/home/username/makevpn.sh $NAME $PASSWORD"
And makevpn.sh contain this:
>./easyrsa sign-req client $NAME
After this run it seems okay but give that error above.
I tried to read after this error and found nothing. :( Hope someone can help because I am hopeless after 4 days of troubleshooting.
Code of VPN script
#!/bin/sh
clear
read -p "Please enter the name of the new certificate : " NAME
read -p "Please enter the Password : " PASSWORD
cd /home/username/EasyRSA-3.0.7/
./easyrsa gen-req $NAME nopass
echo "gen-req done"
cp /home/username/EasyRSA-3.0.7/pki/private/$NAME.key /home/username/client-configs/keys/
echo "cp done"
sshpass -p $PASSWORD scp /home/username/EasyRSA-3.0.7/pki/reqs/$NAME.req username#192.168.1.105:/tmp
echo "scp done"
sshpass -p $PASSWORD ssh username#192.168.1.105 "/home/username/makevpn.sh $NAME $PASSWORD"
echo "ssh done"
cp /tmp/$NAME.crt /home/username/client-configs/keys/
echo "last CP done"
sudo /home/username/client-configs/make_config.sh $NAME
echo "All Done"
Code on CA server
#!/bin/sh
NAME=$1
PASSWORD=$2
cd /home/username/EasyRSA-3.0.7/
echo "CD Done"
./easyrsa import-req /tmp/$NAME.req $NAME
echo "Import-req done"
./easyrsa sign-req client $NAME
echo "Sign-req done"
sshpass -p $PASSWORD scp /home/username/EasyRSA-3.0.7/pki/issued/$NAME.crt username#192.168.1.103:/tmp
echo "Scp done"
I was just browsing the code of that easyrsa script here. This one is likely different from yours given the line for the error is 341. On the Github page, it is line 352 and it is part of a function called cleanup. It appears that this function is only attached as a trap (line 2744). Traps are used to catch signals like sigint (interrupt) which is normally sent on the terminal with ctrl+c (and may display a character like ^C). The reason the error only displays in your script is it likely causes a signal to be emitted that you would not normally receive if you ran it manually over ssh.
The error itself is really not an issue.
Code from Github:
Line 352:
(stty echo 2>/dev/null) || { (set -o echo 2>/dev/null) && set -o echo; }
Line 2744:
trap "cleanup" EXIT
It appears that line is just trying to turn terminal output of your typed characters back on (via stty echo). Sometimes programs will disable terminal output somewhere, and then re-enable it when the program finishes. However, if you were to kill the program mid way through (e.g. with ctrl+c), your program would terminate with the terminal output still disabled. This would make the terminal appear to be frozen. It would still work, but would not display the characters you type with your keyboard. The point of the trap is to ensure that terminal output is re-enabled no matter how the program exits.
More info...
At line 567 there is a function that disables echo. Looks like the point is to not show a password to the screen. If you were to kill the program during password reading, echo would remain disabled on the terminal. Likely the reason for the error has more to do with the way you are running the script. For whatever reason it causes stty echo to fail. Line 352 is assuming that the failure is due to stty echo not being a valid command. So on failure ( || ), it tries a different method (set -o echo) of enabling echo. If I try to run that on my terminal, I also get an error (bash 4.2):
-bash: set: echo: invalid option name

bash read builtin does not echo input if script is piped to less

I stumbled upon this strange behavior of the bash builtin read.
I have a interactive script which has the potential of generating a large output. So naturally you append | less to it.
The script will still ask you for your input but it will not echo what you typed.
Here is a small sample.sh:
#!/bin/bash
echo "Type:"
read -r input
echo "Typed: ${input}"
sample.sh | less
I noticed that this is not a general issue with pipes (e.g. |cat works).
Any clue would be appreciated.
A SOLUTION which works for me:
#!/bin/bash
STTY_ORIG="$(stty -g)" # save stty settings
stty echo # enable echo
echo "Type:"
read -e -r input # use readline (backspace will not work otherwise)
echo "Typed: ${input}"
stty "${STTY_ORIG}" # restore stty settings
A SOLUTION which works for me and did not show an side effects.
Basically just tweak and restore the terminal settings...
#!/bin/bash
STTY_ORIG="$(stty -g)" # save stty settings
stty echo # enable echo
echo "Type:"
read -e -r input # use readline (backspace will not work otherwise)
echo "Typed: ${input}"
stty "${STTY_ORIG}" # restore stty settings
It actualy works for me.
The same script
martus#makus-pc:/tmp/src$ dpkg -l | grep bash
ii bash 4.4-5 amd64 GNU Bourne Again SHell
martus#makus-pc:/tmp/src$ uname -a
Linux makus-pc 4.9.0-4-amd64 #1 SMP Debian 4.9.65-3+deb9u1 (2017-12-23) x86_64 GNU/Linux
Edit: Does the script works without piping less? Less won't show anything typed untill you hit enter.

Password hiding in shell script

I have created a shell script as given below.And I need to hide the password, which i will be given when the script prompt for that. If I run the below script written inside the single quotes in my local machine, it will hide the password. But if I add the SSH part it wont.
What should I do for hiding password ?
ssh root#10.3.2.0 'echo -n Password:;
read -s password;
echo;
echo $password;
'
Try using the -t option to ssh to force tty allocation.
ssh -t root#10.3.2.0 'echo -n Password:;
read -s password;
echo;
echo $password;'
EDIT: explanation of suggested solution
If not running a login-session but just a command instead by default SSH won't allocate a pseudo terminal device for the running process.
This behavior is not a bug a all, but delibarately chosen by design.
This way it is possible to pass binary data uninterpreted between different machines.
Maybe something like:
ssh whoever#wherever 'cat remote_file' | local_program
Passing data between two machines this way would almost be impossible if the data was filtered by a terminal driver in between - Think of the tons of escape sequences you would have to care about !
Without any terminal allocated there is no way of hiding user input. Any attempts to stty something will fail, as there isn't any terminal at all!
To make ssh allocate a pseudo terminal even if not running in a login-session you have to pass the -t parameter to force tty allocation.
Better use ssh keys, that way, no need to pass a password with the help of a ssh-agent. see a good how-to : https://wiki.archlinux.org/index.php/SSH_Keys
I am new to scripts, though I guess this might can help you :
#!/bin/sh
echo -n "Enter Password : "
stty -echo
read password < /dev/tty
stty echo
echo
echo -n "Enter Password again : "
stty -echo
read password1 < /dev/tty
stty echo
echo
echo "Password Entered are : $password and $password1"

Resources