joyent/node-triton#150 fallback to ~/.ssh during docker profile creation
Reviewed by: Todd Whiteman <todd.whiteman@joyent.com> Approved by: Todd Whiteman <todd.whiteman@joyent.com>
This commit is contained in:
parent
86c689e809
commit
dba8915d4f
@ -359,7 +359,7 @@ function _createProfile(opts, cb) {
|
||||
console.log(wrap80('This section will setup authentication to ' +
|
||||
'Triton DataCenter\'s Docker endpoint using your account ' +
|
||||
'and key information specified above. This is only required ' +
|
||||
'if you intend to use `docker` with this profile.'));
|
||||
'if you intend to use `docker` with this profile.\n'));
|
||||
|
||||
profilecommon.profileDockerSetup({
|
||||
cli: cli,
|
||||
|
@ -141,15 +141,11 @@ function setCurrentProfile(opts, cb) {
|
||||
* - {Boolean} implicit: Optional. Boolean indicating if the Docker setup
|
||||
* is implicit (e.g. as a default part of `triton profile create`). If
|
||||
* implicit, we silently skip if ListServices shows no Docker service.
|
||||
* - {Object} keyPaths: Optional. An object with `private` and/or `public`
|
||||
* properties pointing to a full path to an SSH private and/or public
|
||||
* key to use for cert signing.
|
||||
*/
|
||||
function profileDockerSetup(opts, cb) {
|
||||
assert.object(opts.cli, 'opts.cli');
|
||||
assert.string(opts.name, 'opts.name');
|
||||
assert.optionalBool(opts.implicit, 'opts.implicit');
|
||||
assert.optionalObject(opts.keyPaths, 'opts.keyPaths');
|
||||
assert.func(cb, 'cb');
|
||||
|
||||
var cli = opts.cli;
|
||||
@ -163,7 +159,7 @@ function profileDockerSetup(opts, cb) {
|
||||
|
||||
vasync.pipeline({arg: {tritonapi: tritonapi}, funcs: [
|
||||
function dockerKeyWarning(arg, next) {
|
||||
console.log(wordwrap('\nWARNING: Docker uses authentication via ' +
|
||||
console.log(wordwrap('WARNING: Docker uses authentication via ' +
|
||||
'client TLS certificates that do not support encrypted ' +
|
||||
'(passphrase protected) keys or SSH agents. If you continue,' +
|
||||
'this profile setup will attempt to write a copy of your ' +
|
||||
@ -254,18 +250,6 @@ function profileDockerSetup(opts, cb) {
|
||||
next();
|
||||
},
|
||||
|
||||
function checkSshPrivKey(arg, next) {
|
||||
try {
|
||||
tritonapi.keyPair.getPrivateKey();
|
||||
} catch (e) {
|
||||
next(new errors.SetupError(format('could not obtain SSH ' +
|
||||
'private key for keypair with fingerprint "%s" ' +
|
||||
'to create Docker certificate.', profile.keyId)));
|
||||
return;
|
||||
}
|
||||
next();
|
||||
},
|
||||
|
||||
/*
|
||||
* Find the `docker` version, if we can. This can be used later to
|
||||
* control some envvars that depend on the docker version.
|
||||
@ -319,6 +303,72 @@ function profileDockerSetup(opts, cb) {
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
* We need the private key to format as a client cert. If this profile's
|
||||
* key was found in the SSH agent (and by default it prefers to take
|
||||
* it from there), then we can't use `tritonapi.keyPair`, because
|
||||
* the SSH agent protocol will not allow us access to the private key
|
||||
* data (by design).
|
||||
*
|
||||
* As a fallback we'll look (via KeyRing) for a local copy of the
|
||||
* private key to use, and then unlock it if necessary.
|
||||
*/
|
||||
function getPrivKey(arg, next) {
|
||||
// If the key pair already works, then use that...
|
||||
try {
|
||||
arg.privKey = tritonapi.keyPair.getPrivateKey();
|
||||
next();
|
||||
return;
|
||||
} catch (_) {
|
||||
// ... else fall through.
|
||||
}
|
||||
|
||||
var kr = new auth.KeyRing();
|
||||
kr.list(function (listErr, keyMap) {
|
||||
if (listErr) {
|
||||
next(listErr);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If our keyId was found, and with the 'homedir' plugin, then
|
||||
* we should have access to the private key (modulo unlocking).
|
||||
*/
|
||||
var keyPairs = keyMap[tritonapi.profile.keyId] || [];
|
||||
var homedirKeyPair;
|
||||
for (var i = 0; i < keyPairs.length; i++) {
|
||||
if (keyPairs[i].plugin === 'homedir') {
|
||||
homedirKeyPair = keyPairs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (homedirKeyPair) {
|
||||
common.promptPassphraseUnlockKey({
|
||||
// Fake the `tritonapi` object, only `.keyPair` is used.
|
||||
tritonapi: {keyPair: homedirKeyPair}
|
||||
}, function (unlockErr) {
|
||||
if (unlockErr) {
|
||||
next(unlockErr);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
arg.privKey = homedirKeyPair.getPrivateKey();
|
||||
} catch (homedirErr) {
|
||||
next(new errors.SetupError(homedirErr, format(
|
||||
'could not obtain SSH private key for keyId ' +
|
||||
'"%s" to create Docker certificate',
|
||||
profile.keyId)));
|
||||
return;
|
||||
}
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
next(new errors.SetupError(format('could not obtain SSH ' +
|
||||
'private key for keyId "%s" to create Docker ' +
|
||||
'certificate', profile.keyId)));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
function genClientCert_dir(arg, next) {
|
||||
arg.dockerCertPath = path.resolve(cli.configDir,
|
||||
@ -327,7 +377,7 @@ function profileDockerSetup(opts, cb) {
|
||||
},
|
||||
function genClientCert_key(arg, next) {
|
||||
arg.keyPath = path.resolve(arg.dockerCertPath, 'key.pem');
|
||||
var data = tritonapi.keyPair.getPrivateKey().toBuffer('pkcs1');
|
||||
var data = arg.privKey.toBuffer('pkcs1');
|
||||
fs.writeFile(arg.keyPath, data, function (err) {
|
||||
if (err) {
|
||||
next(new errors.SetupError(err, format(
|
||||
@ -340,9 +390,8 @@ function profileDockerSetup(opts, cb) {
|
||||
function genClientCert_cert(arg, next) {
|
||||
arg.certPath = path.resolve(arg.dockerCertPath, 'cert.pem');
|
||||
|
||||
var privKey = tritonapi.keyPair.getPrivateKey();
|
||||
var id = sshpk.identityFromDN('CN=' + profile.account);
|
||||
var cert = sshpk.createSelfSignedCertificate(id, privKey);
|
||||
var cert = sshpk.createSelfSignedCertificate(id, arg.privKey);
|
||||
var data = cert.toBuffer('pem');
|
||||
|
||||
fs.writeFile(arg.certPath, data, function (err) {
|
||||
|
Reference in New Issue
Block a user