J'ai une application qui exécute divers trucs amusants avec Git (comme exécuter git clone & git Push) et j'essaie de la docker.
Je rencontre un problème cependant, où je dois pouvoir ajouter une clé SSH au conteneur pour que le conteneur «utilisateur» puisse l'utiliser.
J'ai essayé de le copier dans /root/.ssh/
, en changeant $HOME
, en créant un wrapper git ssh, et toujours pas de chance.
Voici le fichier Dockerfile pour référence:
#DOCKER-VERSION 0.3.4
from ubuntu:12.04
RUN apt-get update
RUN apt-get install python-software-properties python g++ make git-core openssh-server -y
RUN add-apt-repository ppa:chris-lea/node.js
RUN echo "deb http://archive.ubuntu.com/ubuntu precise universe" >> /etc/apt/sources.list
RUN apt-get update
RUN apt-get install nodejs -y
ADD . /src
ADD ../../home/ubuntu/.ssh/id_rsa /root/.ssh/id_rsa
RUN cd /src; npm install
EXPOSE 808:808
CMD [ "node", "/src/app.js"]
app.js
exécute les commandes git telles que git pull
Si vous utilisez Ubuntu, ssh_config n’est pas correct. Vous devez ajouter
RUN echo " IdentityFile ~/.ssh/id_rsa" >> /etc/ssh/ssh_config
à votre fichier Docker afin qu’il reconnaisse votre clé ssh.
C'est un problème plus difficile si vous devez utiliser SSH au moment de la compilation. Par exemple, si vous utilisez git clone
ou, dans mon cas, pip
et npm
à télécharger depuis un référentiel privé.
La solution que j'ai trouvée consiste à ajouter vos clés à l'aide de l'indicateur --build-arg
. Vous pouvez ensuite utiliser la nouvelle commande expérimentale --squash
(version 1.13 ajoutée) pour fusionner les couches afin que les clés ne soient plus disponibles après leur suppression. Voici ma solution:
Commande de construction
$ docker build -t example --build-arg ssh_prv_key="$(cat ~/.ssh/id_rsa)" --build-arg ssh_pub_key="$(cat ~/.ssh/id_rsa.pub)" --squash .
Dockerfile
FROM python:3.6-slim
ARG ssh_prv_key
ARG ssh_pub_key
RUN apt-get update && \
apt-get install -y \
git \
openssh-server \
libmysqlclient-dev
# Authorize SSH Host
RUN mkdir -p /root/.ssh && \
chmod 0700 /root/.ssh && \
ssh-keyscan github.com > /root/.ssh/known_hosts
# Add the keys and set permissions
RUN echo "$ssh_prv_key" > /root/.ssh/id_rsa && \
echo "$ssh_pub_key" > /root/.ssh/id_rsa.pub && \
chmod 600 /root/.ssh/id_rsa && \
chmod 600 /root/.ssh/id_rsa.pub
# Avoid cache purge by adding requirements first
ADD ./requirements.txt /app/requirements.txt
WORKDIR /app/
RUN pip install -r requirements.txt
# Remove SSH keys
RUN rm -rf /root/.ssh/
# Add the rest of the files
ADD . .
CMD python manage.py runserver
Mise à jour: Si vous utilisez Docker 1.13 et que vous disposez de fonctionnalités expérimentales, vous pouvez ajouter --squash
à la commande de construction qui fusionnera les couches, en supprimant les clés SSH et en les masquant de docker history
.
Note: utilisez cette approche uniquement pour les images qui sont private et qui seront toujours!
La clé ssh reste stockée dans l'image, même si vous la supprimez après l'avoir ajoutée (voir les commentaires dans this post ).
Dans mon cas, c’est bien, c’est ce que j’utilise:
# Setup for ssh onto github
RUN mkdir -p /root/.ssh
ADD id_rsa /root/.ssh/id_rsa
RUN chmod 700 /root/.ssh/id_rsa
RUN echo "Host github.com\n\tStrictHostKeyChecking no\n" >> /root/.ssh/config
Si vous utilisez docker compose un choix simple consiste à transférer l'agent SSH comme suit:
something:
container_name: something
volumes:
- $SSH_AUTH_SOCK:/ssh-agent # Forward local machine SSH key to docker
environment:
SSH_AUTH_SOCK: /ssh-agent
Pour injecter votre clé ssh, dans un conteneur, vous avez plusieurs solutions:
En utilisant un fichier Docker avec l’instruction ADD
, vous pouvez l’injecter pendant votre processus de construction.
Faire simplement quelque chose comme cat id_rsa | docker run -i <image> sh -c 'cat > /root/.ssh/id_rsa'
Utilisation de la commande docker cp
qui vous permet d’injecter des fichiers pendant l’exécution d’un conteneur.
Expanding Réponse de Peter Grainger J'ai pu utiliser la construction en plusieurs étapes disponible depuis Docker 17.05. La page officielle indique:
Avec les versions en plusieurs étapes, vous utilisez plusieurs instructions
FROM
dans votre fichier Docker. Chaque instructionFROM
peut utiliser une base différente et chacune d'entre elles commence une nouvelle étape de la construction. Vous pouvez copier de manière sélective des artefacts d’un stade à l’autre en laissant derrière vous tout ce que vous ne voulez pas dans l’image finale.
Gardant cela à l’esprit, voici mon exemple de Dockerfile
comprenant trois étapes de construction. Il s'agit de créer une image de production de l'application Web client.
# Stage 1: get sources from npm and git over ssh
FROM node:carbon AS sources
ARG SSH_KEY
ARG SSH_KEY_PASSPHRASE
RUN mkdir -p /root/.ssh && \
chmod 0700 /root/.ssh && \
ssh-keyscan bitbucket.org > /root/.ssh/known_hosts && \
echo "${SSH_KEY}" > /root/.ssh/id_rsa && \
chmod 600 /root/.ssh/id_rsa
WORKDIR /app/
COPY package*.json yarn.lock /app/
RUN eval `ssh-agent -s` && \
printf "${SSH_KEY_PASSPHRASE}\n" | ssh-add $HOME/.ssh/id_rsa && \
yarn --pure-lockfile --mutex file --network-concurrency 1 && \
rm -rf /root/.ssh/
# Stage 2: build minified production code
FROM node:carbon AS production
WORKDIR /app/
COPY --from=sources /app/ /app/
COPY . /app/
RUN yarn build:prod
# Stage 3: include only built production files and Host them with Node Express server
FROM node:carbon
WORKDIR /app/
RUN yarn add express
COPY --from=production /app/dist/ /app/dist/
COPY server.js /app/
EXPOSE 33330
CMD ["node", "server.js"]
.dockerignore
répète le contenu du fichier .gitignore
(cela empêche la copie des répertoires node_modules
et dist
résultants du projet):
.idea
dist
node_modules
*.log
Exemple de commande pour construire une image:
$ docker build -t ezze/geoport:0.6.0 \
--build-arg SSH_KEY=$(cat ~/.ssh/id_rsa) \
--build-arg SSH_KEY_PASSPHRASE=my_super_secret \
./
Si votre clé SSH privée n'a pas de phrase secrète, spécifiez un argument SSH_KEY_PASSPHRASE
vide.
Voilà comment cela fonctionne:
1). Lors de la première étape, seuls les fichiers package.json
, yarn.lock
et la clé privée SSH sont copiés dans la première image intermédiaire nommée sources
. Afin d'éviter d'autres invites de phrase secrète de clé SSH, celle-ci est automatiquement ajoutée à ssh-agent
. Enfin, la commande yarn
installe toutes les dépendances requises à partir de NPM et clone les référentiels git privés de Bitbucket via SSH.
2) La deuxième étape construit et réduit le code source de l'application Web et le place dans le répertoire dist
de la prochaine image intermédiaire nommée production
. Notez que le code source de node_modules
installé est copié à partir de l'image nommée sources
produite au premier étage par cette ligne:
COPY --from=sources /app/ /app/
Probablement cela pourrait aussi être la ligne suivante:
COPY --from=sources /app/node_modules/ /app/node_modules/
Nous avons seulement le répertoire node_modules
de la première image intermédiaire ici, plus aucun argument SSH_KEY
et SSH_KEY_PASSPHRASE
. Tout le reste nécessaire à la construction est copié à partir de notre répertoire de projet.
3) Dans la troisième étape, nous réduisons la taille de l'image finale qui sera étiquetée ezze/geoport:0.6.0
en n'incluant que le répertoire dist
de la deuxième image intermédiaire nommée production
et en installant Node Express pour le démarrage d'un serveur Web.
La liste des images donne une sortie comme celle-ci:
REPOSITORY TAG IMAGE ID CREATED SIZE
ezze/geoport 0.6.0 8e8809c4e996 3 hours ago 717MB
<none> <none> 1f6518644324 3 hours ago 1.1GB
<none> <none> fa00f1182917 4 hours ago 1.63GB
node carbon b87c2ad8344d 4 weeks ago 676MB
où les images non marquées correspondent aux première et deuxième étapes de construction intermédiaires.
Si vous courez
$ docker history ezze/geoport:0.6.0 --no-trunc
vous ne verrez aucune mention de SSH_KEY
et SSH_KEY_PASSPHRASE
dans l'image finale.
Cette ligne est un problème:
ADD ../../home/ubuntu/.ssh/id_rsa /root/.ssh/id_rsa
Lorsque vous spécifiez les fichiers que vous souhaitez copier dans l'image, vous ne pouvez utiliser que des chemins relatifs, relatifs au répertoire contenant votre fichier Docker. Donc, vous devriez plutôt utiliser:
ADD id_rsa /root/.ssh/id_rsa
Et placez le fichier id_rsa dans le même répertoire que votre fichier Docker.
Regardez ceci pour plus de détails: http://docs.docker.io/reference/builder/#add
Les conteneurs Docker doivent être considérés comme des «services» propres. Pour séparer les préoccupations, vous devez séparer les fonctionnalités:
1) Les données doivent être dans un conteneur de données: utilisez un volume lié pour cloner le référentiel. Ce conteneur de données peut ensuite être lié au service qui en a besoin.
2) Utilisez un conteneur pour exécuter la tâche de clonage git (c'est-à-dire que seul le travail est un clonage) en liant le conteneur de données à celui-ci lorsque vous l'exécutez.
3) Idem pour ssh-key: mettez-le en volume (comme suggéré ci-dessus) et associez-le au service git clone quand vous en avez besoin
De cette façon, la tâche de clonage et la clé sont éphémères et ne sont actives que lorsque cela est nécessaire.
Désormais, si votre application elle-même est une interface git, vous pouvez envisager d’utiliser les API github ou bitbucket REST directement pour effectuer votre travail: c’est pour cela qu’elles ont été conçues.
Nous avions un problème similaire lors de l’installation de npm au moment de la compilation de docker.
Inspiré de la solution de Daniel van Flymen et en le combinant avec git url rewrite , nous avons trouvé une méthode un peu plus simple pour authentifier l’installation de npm depuis un dépôt privé github - nous avons utilisé oauth2 tokens au lieu des clés.
Dans notre cas, les dépendances npm ont été spécifiées comme "git + https://github.com/ ..."
Pour l'authentification dans le conteneur, les URL doivent être réécrites pour convenir à l'authentification ssh (ssh: //[email protected]/) ou à l'authentification par jeton (https: // $ {GITHUB_TOKEN} @ github.com /).
Commande de construction:
docker build -t sometag --build-arg GITHUB_TOKEN=$GITHUB_TOKEN .
Malheureusement, je suis sur docker 1.9, donc l'option --squash n'est pas encore disponible, elle doit éventuellement être ajoutée
Dockerfile:
FROM node:5.10.0
ARG GITHUB_TOKEN
#Install dependencies
COPY package.json ./
# add rewrite rule to authenticate github user
RUN git config --global url."https://${GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/"
RUN npm install
# remove the secret token from the git config file, remember to use --squash option for docker build, when it becomes available in docker 1.13
RUN git config --global --unset url."https://${GITHUB_TOKEN}@github.com/".insteadOf
# Expose the ports that the app uses
EXPOSE 8000
#Copy server and client code
COPY server /server
COPY clients /clients
Cette question est vraiment ennuyeuse. Comme vous ne pouvez ajouter/copier aucun fichier en dehors du contexte dockerfile, il est donc impossible de lier ~/.ssh/id_rsa au fichier /root/.ssh/id_rsa de l'image, et quand vous avez vraiment besoin d'une clé comme git clone depuis un lien de repo privé ..., lors de la création de votre image de menu fixe.
Quoi qu’il en soit, j’ai trouvé une solution à ce problème, moins convaincante, mais qui a fonctionné pour moi.
dans votre fichier docker:
un script à faire en un tournage:
chaque fois que vous devez exécuter un conteneur à partir de cette image avec certaines exigences de ssh, ajoutez simplement -v pour la commande d'exécution, comme suit:
docker run -v ~/.ssh/id_rsa: /root/.ssh/id_rsa --name commande d'image conteneur
Cette solution n'entraîne aucune clé privée dans votre source de projet et dans l'image du menu fixe, vous évitant ainsi tout problème de sécurité.
Transférez le socket d’authentification ssh vers le conteneur:
docker run --rm -ti \
-v $SSH_AUTH_SOCK:/tmp/ssh_auth.sock \
-e SSH_AUTH_SOCK=/tmp/ssh_auth.sock \
-w /src \
my_image
Votre script pourra effectuer un git clone
.
Extra: Si vous voulez que les fichiers clonés appartiennent à un utilisateur spécifique, vous devez utiliser chown
car utiliser un autre utilisateur que root dans le conteneur fera échouer git
.
Vous pouvez faire cette publication dans l'environnement du conteneur à l'aide de variables supplémentaires:
docker run ...
-e OWNER_USER=$(id -u) \
-e OWNER_GROUP=$(id -g) \
...
Après avoir cloné, vous devez exécuter chown $OWNER_USER:$OWNER_GROUP -R <source_folder>
pour définir la propriété appropriée avant de quitter le conteneur afin que les fichiers soient accessibles à un utilisateur non root en dehors du conteneur.
"vous pouvez laisser sélectivement les serveurs distants accéder à votre agent ssh local comme s'il était exécuté sur le serveur"
https://developer.github.com/guides/using-ssh-agent-forwarding/
Une solution consiste à monter les clés ssh de l'hôte dans le menu fixe avec les options suivantes:
docker run -v /home/<Host user>/.ssh:/home/<docker user>/.ssh <image>
Similaire à la solution ci-dessus. Mais fonctionne avec un utilisateur non root. Fonctionne parfaitement avec github.
Vous pouvez également lier votre répertoire .ssh entre l'hôte et le conteneur. Je ne sais pas si cette méthode a des implications en matière de sécurité, mais il peut s'agir de la méthode la plus simple. Quelque chose comme ça devrait marcher:
$ Sudo docker run -it -v /root/.ssh:/root/.ssh someimage bash
N'oubliez pas que docker fonctionne avec Sudo (à moins que vous ne le fassiez), si c'est le cas, vous utiliserez les clés ssh racines.
J'ai rencontré le même problème aujourd'hui et une version légèrement modifiée avec les publications précédentes. Cette approche m'a paru plus utile
docker run -it -v ~/.ssh/id_rsa:/root/.my-key:ro image /bin/bash
(Notez que readonly signale que le conteneur ne gâchera jamais ma clé ssh.)
À l'intérieur du conteneur, je peux maintenant exécuter:
ssh-agent bash -c "ssh-add ~/.my-key; git clone <gitrepourl> <target>"
Donc, je ne reçois pas cette erreur Bad owner or permissions on /root/.ssh/..
qui a été notée par @kross
Comme eczajk l'a déjà mentionné dans la réponse de Daniel van Flymen, il ne semble pas prudent de retirer les clés et d'utiliser --squash
, car elles seront toujours visibles dans l'historique (docker history --no-trunc
).
Au lieu de cela, avec Docker 18.09, vous pouvez maintenant utiliser la fonctionnalité "construire des secrets". Dans mon cas, j'ai cloné un dépôt privé git en utilisant la clé SSH de mon hôte avec les éléments suivants dans mon fichier Docker:
# syntax=docker/dockerfile:experimental
[...]
RUN --mount=type=ssh git clone [...]
[...]
Pour pouvoir utiliser cela, vous devez activer le nouveau backend BuildKit avant d'exécuter docker build
:
export DOCKER_BUILDKIT=1
Et vous devez ajouter le paramètre --ssh default
à docker build
.
Plus d'infos ici: https://medium.com/@tonistiigi/build-secrets-and-ssh-forwarding-in-docker-18-09-ae8161d066
Dans les versions ultérieures de docker (17.05), vous pouvez utiliser les constructions à plusieurs étages . Quelle est l'option la plus sûre, car les versions précédentes ne peuvent être utilisées que par les versions suivantes et sont ensuite détruites
Voir la réponse à ma question sur le stackoverflow pour plus d'informations
Vous pouvez transmettre les clés autorisées à votre conteneur à l'aide d'un dossier partagé et définir des autorisations à l'aide d'un fichier de menu fixe comme ceci:
FROM ubuntu:16.04
RUN apt-get install -y openssh-server
RUN mkdir /var/run/sshd
EXPOSE 22
RUN cp /root/auth/id_rsa.pub /root/.ssh/authorized_keys
RUN rm -f /root/auth
RUN chmod 700 /root/.ssh
RUN chmod 400 /root/.ssh/authorized_keys
RUN chown root. /root/.ssh/authorized_keys
CMD /usr/sbin/sshd -D
Et votre docker contient quelque chose comme ceci pour partager un répertoire d’authentification sur l’hôte (contenant les authorised_keys) avec le conteneur puis ouvrir le port ssh qui sera accessible via le port 7001 sur l’hôte.
-d -v /home/thatsme/dockerfiles/auth:/root/auth -–publish=127.0.0.1:7001:22
Vous voudrez peut-être consulter https://github.com/jpetazzo/nsenter , qui semble être un autre moyen d’ouvrir un shell sur un conteneur et d’exécuter des commandes dans un conteneur.
Si vous ne vous souciez pas de la sécurité de vos clés SSH, il y a beaucoup de bonnes réponses ici. Si vous le faites, la meilleure réponse que j'ai trouvée était celle d'un lien dans un commentaire ci-dessus vers ce commentaire GitHub par diegocsandrim . Pour que les autres aient plus de chances de le voir, et juste au cas où ce repo disparaîtrait, voici une version révisée de cette réponse:
La plupart des solutions ici finissent par laisser la clé privée dans l'image. C'est mauvais, car toute personne ayant accès à l'image a accès à votre clé privée. Comme nous ne connaissons pas suffisamment le comportement de squash
, il se peut que cela se produise même si vous supprimez la clé et écrasez ce calque.
Nous générons une URL de pré-signature pour accéder à la clé avec aws s3 cli et limitons l'accès pendant environ 5 minutes. Nous sauvegardons cette URL de pré-signature dans un fichier du répertoire repo, puis nous l'ajoutons à l'image dans le fichier dockerfile.
Dans dockerfile, nous avons une commande RUN qui effectue toutes ces opérations: utilisez l'URL de pré-sélection pour obtenir la clé ssh, exécutez npm install et supprimez la clé ssh.
En effectuant cela dans une seule commande, la clé ssh ne serait stockée dans aucune couche, mais l'URL de pré-signature serait stockée, ce qui ne posait pas de problème, car l'URL ne serait plus valide après 5 minutes.
Le script de construction ressemble à:
# build.sh
aws s3 presign s3://my_bucket/my_key --expires-in 300 > ./pre_sign_url
docker build -t my-service .
Dockerfile ressemble à ceci:
FROM node
COPY . .
RUN eval "$(ssh-agent -s)" && \
wget -i ./pre_sign_url -q -O - > ./my_key && \
chmod 700 ./my_key && \
ssh-add ./my_key && \
ssh -o StrictHostKeyChecking=no [email protected] || true && \
npm install --production && \
rm ./my_key && \
rm -rf ~/.ssh/*
ENTRYPOINT ["npm", "run"]
CMD ["start"]
J'essaie de résoudre le problème autrement: en ajoutant une clé publique ssh à une image. Mais lors de mes essais, j'ai découvert que "docker cp" sert à copier d'un conteneur à un hôte. Le point 3 de la réponse semble vouloir dire que vous pouvez utiliser docker cp pour injecter des fichiers dans un conteneur. Voir https://docs.docker.com/engine/reference/commandline/cp/
extrait
Copier des fichiers/dossiers du système de fichiers d’un conteneur dans le chemin d’hôte . Les chemins sont relatifs à la racine du système de fichiers.
Usage: docker cp CONTAINER:PATH HOSTPATH Copy files/folders from the PATH to the HOSTPATH
Pour ce faire, utilisez un moyen simple et sécurisé sans enregistrer votre clé dans un calque d'image Docker, ni en suivant la gymnastique ssh_agent:
L'une des étapes de votre Dockerfile
, créez un répertoire .ssh
en ajoutant:
RUN mkdir -p /root/.ssh
En dessous, vous indiquez que vous souhaitez monter le répertoire ssh en tant que volume:
VOLUME [ "/root/.ssh" ]
Assurez-vous que le ssh_config
de votre conteneur sait où trouver les clés publiques en ajoutant cette ligne:
RUN echo " IdentityFile /root/.ssh/id_rsa" >> /etc/ssh/ssh_config
Exposez le répertoire .ssh
de votre utilisateur local au conteneur au moment de l'exécution:
docker run -v ~/.ssh:/root/.ssh -it image_name
Ou, dans votre dockerCompose.yml
, ajoutez ceci sous la clé de volume du service:
- "~/.ssh:/root/.ssh"
Votre Dockerfile
finale devrait contenir quelque chose comme:
FROM node:6.9.1
RUN mkdir -p /root/.ssh
RUN echo " IdentityFile /root/.ssh/id_rsa" >> /etc/ssh/ssh_config
VOLUME [ "/root/.ssh" ]
EXPOSE 3000
CMD [ "launch" ]
Vous pouvez utiliser des secrets pour gérer les données sensibles dont un conteneur a besoin au moment de l'exécution, mais vous ne souhaitez pas les stocker dans l'image ou dans le contrôle de source, tels que:
- Noms d'utilisateur et mots de passe
- Certificats et clés TLS
- Clés SSH
- Autres données importantes telles que le nom d'une base de données ou d'un serveur interne
- Chaînes génériques ou contenu binaire (taille maximale de 500 Ko)
J'essayais de comprendre comment ajouter des clés de signature à un conteneur à utiliser lors de l'exécution (et non de la construction) et suis tombé sur cette question. Les secrets de Docker semblent être la solution pour mon cas d'utilisation, et comme personne ne l'a encore mentionné, je vais l'ajouter.
Le moyen le plus simple, obtenir un compte tableau de bord et utiliser: ssh-import-id
Certes, nous serons en retard pour la partie, ce qui rendra vos clés du système d’exploitation hôte disponibles pour s’installer à l’intérieur du conteneur, à la volée:
docker run -v ~/.ssh:/mnt -it my_image /bin/bash -c "ln -s /mnt /root/.ssh; ssh [email protected]"
Je ne suis pas en faveur de l'utilisation de Dockerfile pour installer des clés, car les itérations de votre conteneur peuvent laisser des clés privées.
Pour debian/root/registered_keys:
RUN set -x && apt-get install -y openssh-server
RUN mkdir /var/run/sshd
RUN mkdir -p /root/.ssh
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN echo "ssh-rsa AAAA....yP3w== rsa-key-project01" >> /root/.ssh/authorized_keys
RUN chmod -R go= /root/.ssh