diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8cc36b8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +* +!index.js +!ONVAULT +!package.json +!credentials diff --git a/Dockerfile b/Dockerfile index 522213f..9553fb6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,12 @@ FROM mhart/alpine-node:0.10 -RUN mkdir -p /vault /usr/src/app +RUN mkdir -p /vault /vault/store /usr/src/app WORKDIR /usr/src/app COPY package.json /usr/src/app/ RUN npm install COPY . /usr/src/app/ +RUN ln -s /usr/src/app/credentials /usr/bin/credentials EXPOSE 3000 VOLUME /vault diff --git a/ONVAULT b/ONVAULT index 54edcf4..d3961e1 100755 --- a/ONVAULT +++ b/ONVAULT @@ -22,6 +22,10 @@ log () { echo -e "${GREEN}[Dockito Vault]${NC} $@" } +# don't go through proxy for accessing vault +no_proxy_old="$no_proxy" +export no_proxy="$VAULT_HOST" + if ! curl -s "${VAULT_URI}/_ping"; then COUNTER=0 echo 'Waiting 10s for dockito/vault to be ready...' @@ -43,21 +47,46 @@ if curl -s "${VAULT_URI}/_ping"; then cp -r ~/.ssh/* $tmp_ssh_vault fi - log "Downloading private keys..." + log "Downloading private keys and git credentials ..." curl -s "${VAULT_URI}/ssh.tgz" | tar -C ~/.ssh/ -zxf - - chown `whoami` ~/.ssh/* - chmod 600 ~/.ssh/* + chown -f `whoami` ~/.ssh/* || true + chmod -f 600 ~/.ssh/* || true - log "Using ssh key: $VAULT_SSH_KEY" + if [ ! -f ~/.ssh/$VAULT_SSH_KEY ]; then + log "Did not find ssh key: $VAULT_SSH_KEY" + else + log "Using ssh key: $VAULT_SSH_KEY" + fi if [[ "$VAULT_SSH_KEY" != "id_rsa" ]]; then # configure the ssh to any host to use this ssh key echo -e "\nHost *\nIdentityFile ~/.ssh/$VAULT_SSH_KEY" >> ~/.ssh/config fi + # download git credential store to temporary location + tmp_git_credential_store=`mktemp ~/.git-credentials-XXXXXX` + curl -s "${VAULT_URI}/git-credentials" > "$tmp_git_credential_store" + + # check number of credentials (== non-blank lines) for seeing if something is wrong + credential_count=`cat "$tmp_git_credential_store" | sed '/^\s*$/d' | wc -l` + if [ $credential_count -eq 0 ]; then + log "Did not find any git https credentials" + else + log "Using $credential_count git https credential(s)" + fi + + # configure git + git_config_old_credential_helper=`git config --global credential.helper` || true + git_config_old_core_askpass=`git config --global core.askpass` || true + git config --global credential.helper "store --file=$tmp_git_credential_store" + git config --global core.askpass true + + # restore 'no_proxy' for executing the actual command + export no_proxy="$no_proxy_old" + log "Executing command: $@" eval $@ - log "Removing private keys..." + log "Removing private keys and git credentials..." rm -rf ~/.ssh/* # copying backup to ssh directory @@ -65,6 +94,13 @@ if curl -s "${VAULT_URI}/_ping"; then cp -r $tmp_ssh_vault/* ~/.ssh rm -rf $tmp_ssh_vault fi + + rm $tmp_git_credential_store + + # restoring old setting + git config --global credential.helper "$git_config_old_credential_helper" + git config --global core.askpass "$git_config_old_core_askpass" + else log "ERROR: Start the dockito/vault container before using ONVAULT!" log "ex: docker run -d -p ${VAULT_HOST}:14242:3000 -v ~/.ssh:/vault/.ssh dockito/vault" diff --git a/credentials b/credentials new file mode 100755 index 0000000..7599e50 --- /dev/null +++ b/credentials @@ -0,0 +1,127 @@ +#!/bin/sh + +# allow overriding location of credential store +: ${CREDENTIALFILE:=/vault/store/git-credentials} + +usage () { + echo "Usage: + $(basename $0) set URL + $(basename $0) remove URL + $(basename $0) list + $(basename $0) clear + +Examples: + $(basename $0) set github.com + $(basename $0) set gitlab.com/foo/bar + $(basename $0) remove github.com +" +} + +# parse arguments +while [ $# -ge 1 ]; do + case "$1" in + set|remove) + if [ $# -eq 2 ]; then + CMD="credentials_$1 $2" + shift + fi + break + ;; + list|clear) + CMD="credentials_$1" + break + ;; + -h|--help) + usage + exit 0 + ;; + *) + usage + exit 1 + ;; + esac + + shift +done + +if [ $# -ne 1 ] || [ -z "$CMD" ]; then + usage + exit 1 +fi + +# helper to url-encode username and password using javascript +# proper escaping for username & password, see: https://gist.github.com/pierrevalade/6025241 +encodeurl () { + node </dev/null` + if [ -n "$matching" ]; then + tempfile=`mktemp -t git-credentials-XXXXXX` + grep -v "@$URL\$" "$CREDENTIALFILE" > "$tempfile" + mv "$tempfile" "$CREDENTIALFILE" + else + echo "No credentials for '$URL'" + exit 1 + fi +} + +credentials_set () { + # remove leading https:// + URL="$( echo "$1" | sed -e 's#^.*://##' )" + + # remove existing entry + matching=`grep "@$URL\$" "$CREDENTIALFILE" 2>/dev/null` + if [ -n "$matching" ]; then + echo "Removing existing entries:" + echo "$matching" | mask_passwords + read -p "Continue? [yN]: " CONFIRM + if ! [ "$CONFIRM" = "y" ]; then + exit 1 + fi + credentials_remove $URL + echo "" + fi + + echo "Adding credentials for 'https://$URL'." + read -p "Username: " USER + read -p "Password: " -s PASSWORD + echo "" + + USER=`encodeurl "$USER"` + PASSWORD=`encodeurl "$PASSWORD"` + + echo "https://$USER:$PASSWORD@$URL" >> "$CREDENTIALFILE" + chmod 600 "$CREDENTIALFILE" +} + +credentials_list () { + if [ -f "$CREDENTIALFILE" ]; then + # mask passwords and sort by URL (which starts after the "@") + cat "$CREDENTIALFILE" | mask_passwords | sort -k 2 -t "@" + fi +} + +credentials_clear () { + echo "" > "$CREDENTIALFILE" +} + + +$CMD diff --git a/index.js b/index.js index 6de406a..dddb947 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,9 @@ var express = require('express'), fs = require('fs'), exec = require('child_process').exec, mime = require('mime'), - path = require('path'); + path = require('path'), + touch = require("touch"), + mkdirp = require('mkdirp'); var host = process.env.HTTP_HOST || '0.0.0.0'; @@ -21,6 +23,7 @@ app.get('/_ping', function (req, res) { Bundle containing all the user's private keys and ssh configuration */ app.get('/ssh.tgz', function (req, res) { + mkdirp("/vault/.ssh"); exec('mktemp -q /tmp/ssh.XXXXXX', function (err, stdout) { var file = stdout.match(/(.+)/)[0]; @@ -33,11 +36,29 @@ app.get('/ssh.tgz', function (req, res) { var filestream = fs.createReadStream(file); filestream.pipe(res); + fs.unlink(file) }); }); }); +/** + Route to get the credenial store + */ +app.get('/git-credentials', function (req, res) { + var file = '/vault/store/git-credentials'; + touch(file); + var filename = path.basename(file); + var mimetype = mime.lookup(file); + + res.setHeader('Content-disposition', 'attachment; filename=' + filename); + res.setHeader('Content-type', mimetype); + + var filestream = fs.createReadStream(file); + filestream.pipe(res); +}); + + /** Route to get the ONVAULT utility to be used during build */ diff --git a/package.json b/package.json index c8bfb35..6ce1e1d 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "license": "ISC", "dependencies": { "express": "^4.12.3", - "mime": "^1.3.4" + "mime": "^1.3.4", + "touch": "^1.0.0", + "mkdirp": "^0.5.1" } }