title: How to fix "Pseudo-terminal will not be allocated because stdin is not a terminal" in Alpine Linux?
body: <p>I am writing a PHP program that runs a variety of shell commands. Sometimes it needs to call <code>su</code>, and by design, I want that to prompt for an elevated privilege password. Using <code>passthru()</code> in PHP works fine for this.</p>
  
  <p>I have chosen to write functional tests only for my program, since it is dependent on <code>ssh</code>, <code>su</code> and other shell commands. I thus want to run the real thing inside PHPUnit to see if it is working as I expect.</p>
  
  <p>Since this requires a SSH server and user account configurations, I have set up the tests to run in Docker. This approach looks like it will be fine - the image builds, when it runs it calls PHPUnit, and then exits. I expect there will be a way I can return a result via Docker to a calling system, such as Travis CI.</p>
  
  <p>I have selected Alpine Linux as my base Docker image, but I am having problems with terminal allocation. I originally thought PHPUnit was getting in the way (see the original version of this question) but I have now narrowed it down to SSH, either run by PHP or even just at the console.</p>
  
  <p>This works fine:</p>
  
  <pre><code>su -c whoami
  </code></pre>
  
  <p>However, this does not:</p>
  
  <pre><code>ssh localhost -t 'su -c whoami'
  </code></pre>
  
  <p>I get:</p>
  
  <blockquote>
    <p>su: must be suid to work properly<br>
    Connection to localhost closed.</p>
  </blockquote>
  
  <p>Note that <code>ssh</code> is set up with passwordless access to localhost (for testing purposes) so the only password I am expecting to enter is the <code>su</code> one.</p>
  
  <p>The manual for <code>ssh</code> suggests that <code>-f</code> is helpful for where passwords to be asked for (the switch "requests ssh to go to background just before command execution") so I try this:</p>
  
  <pre><code>ssh localhost -t -f 'su -c whoami'
  </code></pre>
  
  <p>And I get:</p>
  
  <blockquote>
    <p>Pseudo-terminal will not be allocated because stdin is not a terminal.<br>
    4275760bde94:~$ su: must be suid to work properly</p>
  </blockquote>
  
  <p>Ah, another error! OK, so <a href="https://stackoverflow.com/questions/7114990/pseudo-terminal-will-not-be-allocated-because-stdin-is-not-a-terminal">I've tried these ideas</a>, particularly forcing a terminal:</p>
  
  <pre><code>ssh localhost -tt -f 'su -c whoami'
  </code></pre>
  
  <p>The double-t still emits complaints about SUID, and still does not work.</p>
  
  <p>However, if I do this on my Ubuntu dev machine (also configured with PPK access to self) it works fine:</p>
  
  <pre><code>$ ssh localhost -t 'su -c whoami'
  Password: 
  root
  Connection to localhost closed.
  </code></pre>
  
  <p>That makes it look like Alpine or BusyBox's OpenSSH is at fault. How can I dig into this further?</p>
  
  <p>One solution is just to swap to another base distro, and Ubuntu would surely work fine, but that will massively inflate my Docker image size (currently on a very nice 68M total). So, I'd like to persist with Alpine for a bit if I can.</p>
  
  <h2>Unlikely to be Docker</h2>
  
  <p>I did wonder whether Docker might be preventing a terminal from being created or attached, but quickly discounted this, since I can get an interactive shell with <code>docker exec -it container_name sh</code>. Furthermore, I can do the <code>ssh</code> and then <code>su</code> in two separate commands inside a Docker shell just fine.</p>
  
  <h2>Bash doesn't help</h2>
  
  <p>I notice that Bash is available in Alpine, but this has not helped either, which surprises me:</p>
  
  <pre><code>/ $ apk add bash
  bash-4.3$ bash
  bash-4.3$ su nonpriv
  bash-4.3$ ssh localhost whoami
  nonpriv
  bash-4.3$ ssh localhost 'su -c whoami'
  su: must be suid to work properly
  bash-4.3$ ssh localhost 'su -s /bin/bash -c whoami'
  su: must be suid to work properly
  bash-4.3$ ssh -t localhost 'su -s /bin/bash -c whoami'
  su: must be suid to work properly
  Connection to localhost closed.
  bash-4.3$ ssh -tt localhost 'su -s /bin/bash -c whoami'
  su: must be suid to work properly
  Connection to localhost closed.
  bash-4.3$ ssh -tf localhost 'su -s /bin/bash -c whoami'
  Pseudo-terminal will not be allocated because stdin is not a terminal.
  bash-4.3$ su: must be suid to work properly
  
  bash-4.3$ ssh -ttf localhost 'su -s /bin/bash -c whoami'
  bash-4.3$ su: must be suid to work properly
  Connection to localhost closed.
  </code></pre>
  
  <h2>Trying shell interpolation</h2>
  
  <p>I have found this nearly works:</p>
  
  <pre><code>/ $ ssh -t localhost "$( su -c whoami )"
  sh: Password:: not found
  sh: root: not found
  Connection to localhost closed.
  </code></pre>
  
  <p>This uses the standard Alpine <code>sh</code> shell, and uses the <code>"$()"</code> construct, found on the aforementioned link, and which I do not fully understand. It outputs <code>Password:</code>, which is the prompt in <code>su</code>, and then waits for the password. When the password is entered the <code>whoami</code> is run, which prints <code>root</code>.</p>
  
  <p>So it looks like it is running the command, but I am not sure if it is running the <code>whoami</code> immediately and then passing it to <code>ssh</code> (not what I want) or whether it is getting a remote shell to localhost then then doing it (this is what I intend).</p>
  
  <p>In any case, it is trying to <em>run</em> stdout lines as if they were commands themselves. How can I get it just to print the output run via SSH?</p>