''su' command in Docker returns 'must be run from terminal'

I'm developing a docker environment for teaching purposes and need to be able to switch users inside docker.

I set up the 'user' user with a password but when I try to switch to it with su, I get "su must be run from terminal".

I get this if I try to ssh into the docker and also by issuing commands through a php shell (an apache service is running on the Docker instance).

Any help is much appreciated.



Solution 1:[1]

When you are ssh-ing in or going in via php your session is not being allocated a pty. I have used each of the following solutions:

ANSWER 1: use ssh -t or ssh -tt to get pty allocated when logging in using ssh:

I had great fun getting commands to run right due to ptys when running sessions like this: jenkins shell -> ssh driver -> ssh test -> docker exec. Good answer here: https://unix.stackexchange.com/questions/105422/command-must-be-run-from-a-terminal

"Try the -t option to ssh. If that does not work try -tt."

"-t Force pseudo-tty 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."

ANSWER 2: use docker run -t ... and docker exec -it

Use the -t and -it options to allocate pty in your docker exec session.

Also with docker exec you can simply use the -u option to login to container as different users and avoid using su. e.g.

$ docker exec -u root -it small_hypatia bash

There is a good question and answer on this here: https://github.com/docker/docker/issues/8631

ANSWER 3: use python to spawn a pty in your shell

Quite a cute hack :)

jenkins@e9fbe94d4c89:~$ su -
su: must be run from a terminal

$ echo "import pty; pty.spawn('/bin/bash')" > /tmp/asdf.py
$ python /tmp/asdf.py

$ su -
Password: 

root@e9fbe94d4c89:~# 

Solution 2:[2]

This solution work by using 'script' command from the 'bsdutiles' package that setup a pty (a terminal). The 'sleep' command is there to prevent sending the password before the 'su' command is ready to read it. The 'tail' command remove the "Password:" input line issued by 'su'.

 sh -c "sleep 1; echo rootpassword" | script -qc 'su -c whoami - root' | tail -n +2

Beware that the rootpassword could be see in many ways (history, ps, /proc/, etc...). Start the command with a space to at least avoid history recording.

Solution 3:[3]

If you use su-exec instead of su the issue with tty completely vanishes since it calls execvp directly instead of forking like su does.

Gosu is another similar alternative.

Solution 4:[4]

"docker -i" didn‘t work for me. I do a merge on both way above, when entering a container, ssh localhost, same effect.

docker exec -it -u user img bash -c "ssh localhost";

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Community
Solution 2 jcamdr
Solution 3 hlovdal
Solution 4 hierfer