From 5000febf72a0b75880a5e02c8631ff0bd6611200 Mon Sep 17 00:00:00 2001 From: Cameron Diver Date: Tue, 20 Jun 2017 11:48:15 +0100 Subject: [PATCH] Read ca files and convert to string before passing to the docker daemon Before this commit, the docker daemon would recieve the filename of the .pem files, which would be interpreted as the body and would fail. This commit ensures that the actual body of the pem files are sent to the daemon. Change-type: patch Connects-to: #562 Signed-off-by: Cameron Diver --- CHANGELOG.md | 4 ++ build/actions/deploy.js | 98 +++++++++++++++++----------------- build/utils/docker.js | 95 ++++++++++++++++++--------------- lib/actions/deploy.coffee | 107 +++++++++++++++++++------------------- lib/utils/docker.coffee | 104 ++++++++++++++++++++---------------- 5 files changed, 219 insertions(+), 189 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc4b7dd6..5937be88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## Fixed + +- Ensure to send .pem file contents rather than filename to docker daemon + ## [5.11.0] - 2017-06-19 ### Added diff --git a/build/actions/deploy.js b/build/actions/deploy.js index 0e942189..c007a04f 100644 --- a/build/actions/deploy.js +++ b/build/actions/deploy.js @@ -202,57 +202,57 @@ module.exports = { tmp.setGracefulCleanup(); logs = ''; upload = function(token, username, url) { - var docker; - docker = dockerUtils.getDocker(options); - return parseInput(params, options).then(function(arg) { - var appName, build, imageName, source; - appName = arg[0], build = arg[1], source = arg[2], imageName = arg[3]; - return tmpNameAsync().then(function(bufferFile) { - options = _.assign({}, options, { - appName: appName - }); - params = _.assign({}, params, { - source: source - }); - return Promise["try"](function() { - if (build) { - return dockerUtils.runBuild(params, options, getBundleInfo, logStreams); - } else { - return { - image: imageName, - log: '' - }; - } - }).then(function(arg1) { - var buildLogs, imageName; - imageName = arg1.image, buildLogs = arg1.log; - logging.logInfo(logStreams, 'Initializing deploy...'); - logs = buildLogs; - return Promise.all([dockerUtils.bufferImage(docker, imageName, bufferFile), token, username, url, params.appName, logStreams]).spread(performUpload); - })["finally"](function() { + return dockerUtils.getDocker(options).then(function(docker) { + return parseInput(params, options).then(function(arg) { + var appName, build, imageName, source; + appName = arg[0], build = arg[1], source = arg[2], imageName = arg[3]; + return tmpNameAsync().then(function(bufferFile) { + options = _.assign({}, options, { + appName: appName + }); + params = _.assign({}, params, { + source: source + }); return Promise["try"](function() { - return require('mz/fs').unlink(bufferFile); - })["catch"](_.noop); + if (build) { + return dockerUtils.runBuild(params, options, getBundleInfo, logStreams); + } else { + return { + image: imageName, + log: '' + }; + } + }).then(function(arg1) { + var buildLogs, imageName; + imageName = arg1.image, buildLogs = arg1.log; + logging.logInfo(logStreams, 'Initializing deploy...'); + logs = buildLogs; + return Promise.all([dockerUtils.bufferImage(docker, imageName, bufferFile), token, username, url, params.appName, logStreams]).spread(performUpload); + })["finally"](function() { + return Promise["try"](function() { + return require('mz/fs').unlink(bufferFile); + })["catch"](_.noop); + }); }); - }); - }).tap(function(arg) { - var buildId, imageName; - imageName = arg.image, buildId = arg.buildId; - logging.logSuccess(logStreams, "Successfully deployed image: " + (formatImageName(imageName))); - return buildId; - }).then(function(arg) { - var buildId, imageName; - imageName = arg.image, buildId = arg.buildId; - if (logs === '' || (options.nologupload != null)) { - return ''; - } - logging.logInfo(logStreams, 'Uploading logs to dashboard...'); - return Promise.join(logs, token, url, buildId, username, params.appName, uploadLogs)["return"]('Successfully uploaded logs'); - }).then(function(msg) { - if (msg !== '') { - return logging.logSuccess(logStreams, msg); - } - }).asCallback(done); + }).tap(function(arg) { + var buildId, imageName; + imageName = arg.image, buildId = arg.buildId; + logging.logSuccess(logStreams, "Successfully deployed image: " + (formatImageName(imageName))); + return buildId; + }).then(function(arg) { + var buildId, imageName; + imageName = arg.image, buildId = arg.buildId; + if (logs === '' || (options.nologupload != null)) { + return ''; + } + logging.logInfo(logStreams, 'Uploading logs to dashboard...'); + return Promise.join(logs, token, url, buildId, username, params.appName, uploadLogs)["return"]('Successfully uploaded logs'); + }).then(function(msg) { + if (msg !== '') { + return logging.logSuccess(logStreams, msg); + } + }).asCallback(done); + }); }; return Promise.join(resin.auth.getToken(), resin.auth.whoami(), resin.settings.get('resinUrl'), upload); } diff --git a/build/utils/docker.js b/build/utils/docker.js index 0796402b..5dd02798 100644 --- a/build/utils/docker.js +++ b/build/utils/docker.js @@ -58,27 +58,35 @@ exports.appendOptions = function(opts) { }; exports.generateConnectOpts = generateConnectOpts = function(opts) { - var connectOpts; - connectOpts = {}; - if ((opts.docker != null) && (opts.dockerHost == null)) { - connectOpts.socketPath = opts.docker; - } else if ((opts.dockerHost != null) && (opts.docker == null)) { - connectOpts.host = opts.dockerHost; - connectOpts.port = opts.dockerPort || 2376; - } else if ((opts.docker != null) && (opts.dockerHost != null)) { - throw new Error("Both a local docker socket and docker host have been provided. Don't know how to continue."); - } else { - connectOpts.socketPath = '/var/run/docker.sock'; - } - if ((opts.ca != null) || (opts.cert != null) || (opts.key != null)) { - if (!((opts.ca != null) && (opts.cert != null) && (opts.key != null))) { - throw new Error('You must provide a CA, certificate and key in order to use TLS'); + var Promise, fs; + Promise = require('bluebird'); + fs = require('mz/fs'); + return Promise["try"](function() { + var connectOpts; + connectOpts = {}; + if ((opts.docker != null) && (opts.dockerHost == null)) { + connectOpts.socketPath = opts.docker; + } else if ((opts.dockerHost != null) && (opts.docker == null)) { + connectOpts.host = opts.dockerHost; + connectOpts.port = opts.dockerPort || 2376; + } else if ((opts.docker != null) && (opts.dockerHost != null)) { + throw new Error("Both a local docker socket and docker host have been provided. Don't know how to continue."); + } else { + connectOpts.socketPath = '/var/run/docker.sock'; } - connectOpts.ca = opts.ca; - connectOpts.cert = opts.cert; - connectOpts.key = opts.key; - } - return connectOpts; + if ((opts.ca != null) || (opts.cert != null) || (opts.key != null)) { + if (!((opts.ca != null) && (opts.cert != null) && (opts.key != null))) { + throw new Error('You must provide a CA, certificate and key in order to use TLS'); + } + return Promise.all([fs.readFile(opts.ca), fs.readFile(opts.cert), fs.readFile(opts.key)]).spread(function(ca, cert, key) { + connectOpts.ca = ca.toString(); + connectOpts.cert = cert.toString(); + connectOpts.key = key.toString(); + return connectOpts; + }); + } + return connectOpts; + }); }; exports.tarDirectory = tarDirectory = function(dir) { @@ -189,7 +197,7 @@ exports.runBuild = function(params, options, getBundleInfo, logStreams) { return tarDirectory(params.source); }).then(function(tarStream) { return new Promise(function(resolve, reject) { - var builder, connectOpts, hooks, opts; + var hooks; hooks = { buildSuccess: function(image) { var doodle; @@ -252,23 +260,25 @@ exports.runBuild = function(params, options, getBundleInfo, logStreams) { return newStream.pipe(logThroughStream).pipe(cacheHighlightStream()).pipe(logStreams.build); } }; - connectOpts = generateConnectOpts(options); - logging.logDebug(logStreams, 'Connecting with the following options:'); - logging.logDebug(logStreams, JSON.stringify(connectOpts, null, ' ')); - builder = new dockerBuild.Builder(connectOpts); - opts = {}; - if (options.tag != null) { - opts['t'] = options.tag; - } - if (options.nocache != null) { - opts['nocache'] = true; - } - if (options.buildArg != null) { - opts['buildargs'] = parseBuildArgs(options.buildArg, function(arg) { - return logging.logWarn(logStreams, "Could not parse variable: '" + arg + "'"); - }); - } - return builder.createBuildStream(opts, hooks, reject); + return generateConnectOpts(options).then(function(connectOpts) { + var builder, opts; + logging.logDebug(logStreams, 'Connecting with the following options:'); + logging.logDebug(logStreams, JSON.stringify(connectOpts, null, ' ')); + builder = new dockerBuild.Builder(connectOpts); + opts = {}; + if (options.tag != null) { + opts['t'] = options.tag; + } + if (options.nocache != null) { + opts['nocache'] = true; + } + if (options.buildArg != null) { + opts['buildargs'] = parseBuildArgs(options.buildArg, function(arg) { + return logging.logWarn(logStreams, "Could not parse variable: '" + arg + "'"); + }); + } + return builder.createBuildStream(opts, hooks, reject); + }); }); }); }; @@ -287,12 +297,13 @@ exports.bufferImage = function(docker, imageId, bufferFile) { }; exports.getDocker = function(options) { - var Docker, Promise, connectOpts; + var Docker, Promise; Docker = require('dockerode'); Promise = require('bluebird'); - connectOpts = generateConnectOpts(options); - connectOpts['Promise'] = Promise; - return new Docker(connectOpts); + return generateConnectOpts(options).then(function(connectOpts) { + connectOpts['Promise'] = Promise; + return new Docker(connectOpts); + }); }; hasQemu = function() { diff --git a/lib/actions/deploy.coffee b/lib/actions/deploy.coffee index 5d311a3c..77eb10c9 100644 --- a/lib/actions/deploy.coffee +++ b/lib/actions/deploy.coffee @@ -175,64 +175,65 @@ module.exports = logs = '' upload = (token, username, url) -> - docker = dockerUtils.getDocker(options) - # Check input parameters - parseInput(params, options) - .then ([appName, build, source, imageName]) -> - tmpNameAsync() - .then (bufferFile) -> + dockerUtils.getDocker(options) + .then (docker) -> + # Check input parameters + parseInput(params, options) + .then ([appName, build, source, imageName]) -> + tmpNameAsync() + .then (bufferFile) -> - # Setup the build args for how the build routine expects them - options = _.assign({}, options, { appName }) - params = _.assign({}, params, { source }) + # Setup the build args for how the build routine expects them + options = _.assign({}, options, { appName }) + params = _.assign({}, params, { source }) - Promise.try -> - if build - dockerUtils.runBuild(params, options, getBundleInfo, logStreams) - else - { image: imageName, log: '' } - .then ({ image: imageName, log: buildLogs }) -> - logging.logInfo(logStreams, 'Initializing deploy...') - - logs = buildLogs - Promise.all [ - dockerUtils.bufferImage(docker, imageName, bufferFile) - token - username - url - params.appName - logStreams - ] - .spread(performUpload) - .finally -> - # If the file was never written to (for instance because an error - # has occured before any data was written) this call will throw an - # ugly error, just suppress it Promise.try -> - require('mz/fs').unlink(bufferFile) - .catch(_.noop) - .tap ({ image: imageName, buildId }) -> - logging.logSuccess(logStreams, "Successfully deployed image: #{formatImageName(imageName)}") - return buildId - .then ({ image: imageName, buildId }) -> - if logs is '' or options.nologupload? - return '' + if build + dockerUtils.runBuild(params, options, getBundleInfo, logStreams) + else + { image: imageName, log: '' } + .then ({ image: imageName, log: buildLogs }) -> + logging.logInfo(logStreams, 'Initializing deploy...') - logging.logInfo(logStreams, 'Uploading logs to dashboard...') + logs = buildLogs + Promise.all [ + dockerUtils.bufferImage(docker, imageName, bufferFile) + token + username + url + params.appName + logStreams + ] + .spread(performUpload) + .finally -> + # If the file was never written to (for instance because an error + # has occured before any data was written) this call will throw an + # ugly error, just suppress it + Promise.try -> + require('mz/fs').unlink(bufferFile) + .catch(_.noop) + .tap ({ image: imageName, buildId }) -> + logging.logSuccess(logStreams, "Successfully deployed image: #{formatImageName(imageName)}") + return buildId + .then ({ image: imageName, buildId }) -> + if logs is '' or options.nologupload? + return '' - Promise.join( - logs - token - url - buildId - username - params.appName - uploadLogs - ) - .return('Successfully uploaded logs') - .then (msg) -> - logging.logSuccess(logStreams, msg) if msg isnt '' - .asCallback(done) + logging.logInfo(logStreams, 'Uploading logs to dashboard...') + + Promise.join( + logs + token + url + buildId + username + params.appName + uploadLogs + ) + .return('Successfully uploaded logs') + .then (msg) -> + logging.logSuccess(logStreams, msg) if msg isnt '' + .asCallback(done) Promise.join( resin.auth.getToken() diff --git a/lib/utils/docker.coffee b/lib/utils/docker.coffee index 0fdf121c..a7dfd025 100644 --- a/lib/utils/docker.coffee +++ b/lib/utils/docker.coffee @@ -70,36 +70,47 @@ exports.appendOptions = (opts) -> ] exports.generateConnectOpts = generateConnectOpts = (opts) -> - connectOpts = {} - # Firsly need to decide between a local docker socket - # and a host available over a host:port combo - if opts.docker? and not opts.dockerHost? - # good, local docker socket - connectOpts.socketPath = opts.docker - else if opts.dockerHost? and not opts.docker? - # Good a host is provided, and local socket isn't - connectOpts.host = opts.dockerHost - connectOpts.port = opts.dockerPort || 2376 - else if opts.docker? and opts.dockerHost? - # Both provided, no obvious way to continue - throw new Error("Both a local docker socket and docker host have been provided. Don't know how to continue.") - else - # None provided, assume default docker local socket - connectOpts.socketPath = '/var/run/docker.sock' + Promise = require('bluebird') + fs = require('mz/fs') + _ = require('lodash') - # Now need to check if the user wants to connect over TLS - # to the host + Promise.try -> + connectOpts = {} + # Firsly need to decide between a local docker socket + # and a host available over a host:port combo + if opts.docker? and not opts.dockerHost? + # good, local docker socket + connectOpts.socketPath = opts.docker + else if opts.dockerHost? and not opts.docker? + # Good a host is provided, and local socket isn't + connectOpts.host = opts.dockerHost + connectOpts.port = opts.dockerPort || 2376 + else if opts.docker? and opts.dockerHost? + # Both provided, no obvious way to continue + throw new Error("Both a local docker socket and docker host have been provided. Don't know how to continue.") + else + # None provided, assume default docker local socket + connectOpts.socketPath = '/var/run/docker.sock' - # If any are set... - if (opts.ca? or opts.cert? or opts.key?) - # but not all - if not (opts.ca? and opts.cert? and opts.key?) - throw new Error('You must provide a CA, certificate and key in order to use TLS') - connectOpts.ca = opts.ca - connectOpts.cert = opts.cert - connectOpts.key = opts.key + # Now need to check if the user wants to connect over TLS + # to the host - return connectOpts + # If any are set... + if (opts.ca? or opts.cert? or opts.key?) + # but not all + if not (opts.ca? and opts.cert? and opts.key?) + throw new Error('You must provide a CA, certificate and key in order to use TLS') + + certBodies = { + ca: fs.readFile(opts.ca, 'utf-8') + cert: fs.readFile(opts.cert, 'utf-8') + key: fs.readFile(opts.key, 'utf-8') + } + return Promise.props(certBodies) + .then (toMerge) -> + _.merge(connectOpts, toMerge) + + return connectOpts exports.tarDirectory = tarDirectory = (dir) -> Promise = require('bluebird') @@ -262,24 +273,25 @@ exports.runBuild = (params, options, getBundleInfo, logStreams) -> .pipe(logStreams.build) # Create a builder - connectOpts = generateConnectOpts(options) + generateConnectOpts(options) + .then (connectOpts) -> - # Allow degugging output, hidden behind an env var - logging.logDebug(logStreams, 'Connecting with the following options:') - logging.logDebug(logStreams, JSON.stringify(connectOpts, null, ' ')) + # Allow degugging output, hidden behind an env var + logging.logDebug(logStreams, 'Connecting with the following options:') + logging.logDebug(logStreams, JSON.stringify(connectOpts, null, ' ')) - builder = new dockerBuild.Builder(connectOpts) - opts = {} + builder = new dockerBuild.Builder(connectOpts) + opts = {} - if options.tag? - opts['t'] = options.tag - if options.nocache? - opts['nocache'] = true - if options.buildArg? - opts['buildargs'] = parseBuildArgs options.buildArg, (arg) -> - logging.logWarn(logStreams, "Could not parse variable: '#{arg}'") + if options.tag? + opts['t'] = options.tag + if options.nocache? + opts['nocache'] = true + if options.buildArg? + opts['buildargs'] = parseBuildArgs options.buildArg, (arg) -> + logging.logWarn(logStreams, "Could not parse variable: '#{arg}'") - builder.createBuildStream(opts, hooks, reject) + builder.createBuildStream(opts, hooks, reject) # Given an image id or tag, export the image to a tar archive, # gzip the result, and buffer it to disk. @@ -298,10 +310,12 @@ exports.bufferImage = (docker, imageId, bufferFile) -> exports.getDocker = (options) -> Docker = require('dockerode') Promise = require('bluebird') - connectOpts = generateConnectOpts(options) - # Use bluebird's promises - connectOpts['Promise'] = Promise - new Docker(connectOpts) + + generateConnectOpts(options) + .then (connectOpts) -> + # Use bluebird's promises + connectOpts['Promise'] = Promise + new Docker(connectOpts) hasQemu = -> fs = require('mz/fs')