Yelp/dumb-init

Support tty / interactive scripts

Closed this issue · 5 comments

My Dockerfile runs a bash script which sometimes need to be interactive (it can run a curl -u USERNAME and expects the user to type in a password).

ENTRYPOINT ["/usr/bin/dumb-init", "--", "/config-and-run.sh"]

That fails with dumb-init, I'm not given a chance to enter my password.

$ docker run -ti myimage
...
Enter host password for user 'jamshid':
curl: (22) The requested URL returned error: 400 Bad Request
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell

Would be nice if dumb-init supported it. FYI a similar project https://github.com/krallin/tini does support it.

Here's tini's PR, seems that we need to handle SIGTTIN and SIGTTOU explicitly: https://github.com/krallin/tini/pull/21/files

@asottile you'll also need to use tcsetpgrp to actually hand over the TTY to the child process group (assuming you already isolate the child into a process group). You can see that in Tini in the isolate_child function.

Handling those signals is meant to ensure you don't lock up your init process if it tries to use the TTY despite not owning it anymore (and don't forward signals designed for your init process to the child). I actually wrote a blog post about this after implementing this in Tini. Might be worth a read! http://curiousthing.org/sigttin-sigttou-deep-dive-linux

I also strongly recommend http://www.linusakesson.net/programming/tty/ for more on this topic.

Cheers,

@jamshid I'm actually having a difficult time reproducing. Could you provide a minimal reproduction? Here's my attempt and output:

FROM ubuntu:xenial

RUN apt-get update && \
    apt-get install -y --no-install-recommends ca-certificates curl wget && \
    apt-get clean

RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.0.2/dumb-init_1.0.2_amd64 && \
    chmod +x /usr/local/bin/dumb-init

RUN /bin/echo -e '#!/bin/bash\n\
curl -v \
    -u DONT_ACTUALLY_TYPE_YOUR_PASSWORD \
    http://example.com > /dev/null' > /run.sh && \
    chmod 755 /run.sh

ENTRYPOINT ["dumb-init", "--", "/run.sh"]

(Thought it's not clear here, It does actually prompt me for a password and blocks on my response)

$ docker build -t foo . && docker run -ti foo bash
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM ubuntu:xenial
 ---> c5f1cf30c96b
Step 2 : RUN apt-get update &&     apt-get install -y --no-install-recommends ca-certificates curl wget &&     apt-get clean
 ---> Using cache
 ---> a80391220675
Step 3 : RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.0.2/dumb-init_1.0.2_amd64 &&     chmod +x /usr/local/bin/dumb-init
 ---> Using cache
 ---> 12aa56b749ec
Step 4 : RUN /bin/echo -e '#!/bin/bash\ncurl -v     -u DONT_ACTUALLY_TYPE_YOUR_PASSWORD     http://example.com > /dev/null' > /run.sh &&     chmod 755 /run.sh
 ---> Running in e302136fed2a
 ---> 5d7f3322950e
Removing intermediate container e302136fed2a
Step 5 : ENTRYPOINT dumb-init -- /run.sh
 ---> Running in fcfac3e3ee64
 ---> 5cebd56c16ce
Removing intermediate container fcfac3e3ee64
Successfully built 5cebd56c16ce
Enter host password for user 'DONT_ACTUALLY_TYPE_YOUR_PASSWORD':
* Rebuilt URL to: http://example.com/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 93.184.216.34...
* Connected to example.com (93.184.216.34) port 80 (#0)
* Server auth using Basic with user 'DONT_ACTUALLY_TYPE_YOUR_PASSWORD'
> GET / HTTP/1.1
> Host: example.com
> Authorization: Basic RE9OVF9BQ1RVQUxMWV9UWVBFX1lPVVJfUEFTU1dPUkQ6
> User-Agent: curl/7.47.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: max-age=604800
< Content-Type: text/html
< Date: Fri, 20 May 2016 15:40:18 GMT
< Etag: "359670651+gzip"
< Expires: Fri, 27 May 2016 15:40:18 GMT
< Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT
< Server: ECS (rhv/81A7)
< Vary: Accept-Encoding
< X-Cache: HIT
< x-ec-custom-error: 1
< Content-Length: 1270
< 
{ [1270 bytes data]
100  1270  100  1270    0     0   9656      0 --:--:-- --:--:-- --:--:--  9621
* Connection #0 to host example.com left intact

Sorry! It's a lot harder to reproduce than I thought, depends on centos7 and weird bash redirects.

Here's a complete example. Run this Dockerfile's image with "docker run -ti", curl is not run interactively.

# https://github.com/Yelp/dumb-init/issues/78
# On centos7 (but ubuntu is fine!), when using a weird combination of stdout/err
# redirects and bash, "dumb-init" will not give a curl command the TTY. Would like
# to be able to enter a password interactively.
#
# EXPECTED:
# docker build -t dumb-init-issue-78 . && docker run -ti dumb-init-issue-78
#  ==> I should be prompted for password and be able to enter it, since "docker run -ti"
#
# BUG:
# docker build -t dumb-init-issue-78 . && docker run -ti dumb-init-issue-78
#  ==> I see the curl password prompt but it immediately scrolls buy
#
# WORKAROUND:
# Using "tini" seems to work, I'm allowed to enter a password interactively to curl.
#
FROM centos:centos7
RUN yum install -y curl wget

# Weird, it works fine (you get the curl password prompt) under ubuntu
#FROM ubuntu:xenial
#RUN apt-get update && \
#    apt-get install -y --no-install-recommends ca-certificates curl wget && \
#    apt-get clean

RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.0.2/dumb-init_1.0.2_amd64 && \
    chmod +x /usr/local/bin/dumb-init

ENV TINI_VERSION v0.9.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini

# /run.sh 
RUN /bin/echo -e '#!/bin/bash\n\
    echo Before messing with stdout/err ; \
    exec 6>&1 7>&2 > >(tee -a /config.log) 2>&1 ; \
    echo After saving stdout/err handles and redirecting to log file ; \
    source /create-token.sh \
    exec >&6 2>&7 # restor stdout/err ; \
    echo After the default stdout/stderr have been restored.' >  /run.sh && chmod 755 /run.sh

RUN /bin/echo -e '#!/bin/bash\n\
  OUTPUT=$(curl -v -u DONT_ACTUALLY_TYPE_YOUR_PASSWORD \
    http://hub.docker.com)' > /create-token.sh \
    chmod 755 /create-token.sh

ENTRYPOINT ["dumb-init", "--", "/run.sh"]
#ENTRYPOINT ["/tini", "-g", "--", "/run.sh"]

@jamshid thanks for that really awesome reproduction! It looks like this is fixed in dumb-init v1.2.0 (which also fixed #51, a similar issue) using that same reproduction.

I'll go ahead and close this, but please feel free to reopen if you're still noticing any trouble with this!