web-dev-qa-db-fra.com

Exécution en tant que utilisateur hôte dans un conteneur

Dans mon équipe, nous utilisons des conteneurs Docker pour exécuter localement les applications de notre site Web pendant que nous développons sur celles-ci.

En supposant que je travaille sur une application Flask à app.py avec des dépendances dans requirements.txt, un flux de travail ressemblerait à peu près à ceci:

# I am "robin" and I am in the docker group
$ whoami
robin
$ groups
robin docker

# Install dependencies into a docker volume
$ docker run -ti -v `pwd`:`pwd` -w `pwd` -v pydeps:/usr/local python:3-slim pip install -r requirements.txt
Collecting Flask==0.12.2 (from -r requirements.txt (line 1))
# ... etc.

# Run the app using the same docker volume
$ docker run -ti -v `pwd`:`pwd` -w `pwd` -v pydeps:/usr/local -e FLASK_APP=app.py -e FLASK_DEBUG=true -p 5000:5000 python:3-slim flask run -h 0.0.0.0
 * Serving Flask app "app"
 * Forcing debug mode on
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 251-131-649

Nous avons maintenant un serveur local qui exécute notre application, et nous pouvons modifier les fichiers locaux et le serveur sera actualisé au besoin.

Dans l'exemple ci-dessus, l'application s'exécute en tant qu'utilisateur root. Ce n'est pas un problème, sauf si l'application écrit les fichiers dans le répertoire de travail. Si tel est le cas, nous pourrions nous retrouver avec des fichiers (par exemple, quelque chose comme cache.sqlite ou debug.log) dans notre répertoire de travail appartenant à root. Cela a causé de nombreux problèmes aux utilisateurs de notre équipe.

Pour nos autres applications, nous avons résolu ce problème en exécutant l’application avec l’UID et le GID hôte _ - par exemple. pour une application Django:

$ docker run -ti -u `id -u`:`id -g` -v `pwd`:`pwd` -w `pwd` -v pydeps:/usr/local -p 8000:8000 python:3-slim ./manage.py runserver

Dans ce cas, l'application s'exécutera en tant qu'utilisateur inexistant avec l'ID 1000 à l'intérieur du conteneur, mais tous les fichiers écrits dans le répertoire Host finissent par appartenir à l'utilisateur robin. Cela fonctionne bien à Django.

Cependant, Flask refuse de s'exécuter en tant qu'utilisateur non existant (en mode débogage):

$ docker run -ti -u `id -u`:`id -g` -v `pwd`:`pwd` -w `pwd` -v pydeps:/usr/local -e FLASK_APP=app.py -e FLASK_DEBUG=true -p 5000:5000 python:3-slim flask run -h 0.0.0.0
 * Serving Flask app "app"
 * Forcing debug mode on
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
Traceback (most recent call last):
...
  File "/usr/local/lib/python3.6/getpass.py", line 169, in getuser
    return pwd.getpwuid(os.getuid())[0]
KeyError: 'getpwuid(): uid not found: 1000'

Est-ce que quelqu'un sait s'il y a un moyen que je puisse soit:

  • Faites en sorte que Flask ne s'inquiète pas de l'ID utilisateur non attribué, ou
  • D'une manière ou d'une autre, attribuez dynamiquement l'ID utilisateur à un nom d'utilisateur au moment de l'exécution, ou
  • Sinon, autorisez l’application de menu fixe à créer des fichiers sur l’hôte en tant qu’utilisateur hôte?

La seule solution à laquelle je puisse penser actuellement (super hacky) est de modifier les autorisations de /etc/passwd dans l'image du menu fixe pour qu'elles soient en écriture, puis d'ajouter une nouvelle ligne à ce fichier au moment de l'exécution pour affecter le nouveau couple UID/GID à un fichier. Nom d'utilisateur.

12
Robin Winslow

Vous pouvez partager le fichier passwd de l'hôte: 

docker run -ti -v /etc/passwd:/etc/passwd -u `id -u`:`id -g` -v `pwd`:`pwd` -w `pwd` -v pydeps:/usr/local -p 8000:8000 python:3-slim ./manage.py runserver

Ou ajoutez l'utilisateur à l'image avec useradd, en utilisant /etc comme volume, de la même manière que vous utilisez /usr/local:

docker run -v etcvol:/etc python..... useradd -u `id -u` $USER

(id -u et $ USER sont tous deux résolus dans le shell d'hôte, avant que le menu fixe reçoive la commande)

18
Robert

Juste frappé ce problème et trouvé une solution de contournement différente. 

De getpass.py :

def getuser():
    """Get the username from the environment or password database.

    First try various environment variables, then the password
    database.  This works on Windows as long as USERNAME is set.

    """


    for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'):
        user = os.environ.get(name)
        if user:
            return user


    # If this fails, the exception will "explain" why
    import pwd
    return pwd.getpwuid(os.getuid())[0]

L'appel à getpwuid() n'est effectué que si aucune des variables d'environnement suivantes n'est définie: LOGNAME, USER, LNAME, USERNAME

La définition de l’un d’eux devrait permettre au conteneur de démarrer.

$ docker run -ti -e USER=someuser ...

Dans mon cas, l'appel à getuser() semble provenir de la bibliothèque Werkzeug qui tente de générer un code PIN pour le débogueur.

1
codemonkey

Si vous pouvez utiliser un autre package Python pour démarrer votre conteneur, vous pouvez peut-être utiliser mon package Python https://github.com/boon-code/docker-inside qui écrase le point d'entrée et crée votre utilisateur dans le conteneur à la volée ...

docker-inside -v `pwd`:`pwd` -w `pwd` -v pydeps:/usr/local -e FLASK_APP=app.py -e FLASK_DEBUG=true -p 5000:5000 python:3-slim -- flask run -h 0.0.0.0

Remplacer le point d’entrée sur la ligne de commande et transmettre un script qui crée votre utilisateur peut également vous convenir, si vous souhaitez conserver Docker CLI.

0
boo-hoo