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 ' +
|
console.log(wrap80('This section will setup authentication to ' +
|
||||||
'Triton DataCenter\'s Docker endpoint using your account ' +
|
'Triton DataCenter\'s Docker endpoint using your account ' +
|
||||||
'and key information specified above. This is only required ' +
|
'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({
|
profilecommon.profileDockerSetup({
|
||||||
cli: cli,
|
cli: cli,
|
||||||
|
@ -141,15 +141,11 @@ function setCurrentProfile(opts, cb) {
|
|||||||
* - {Boolean} implicit: Optional. Boolean indicating if the Docker setup
|
* - {Boolean} implicit: Optional. Boolean indicating if the Docker setup
|
||||||
* is implicit (e.g. as a default part of `triton profile create`). If
|
* is implicit (e.g. as a default part of `triton profile create`). If
|
||||||
* implicit, we silently skip if ListServices shows no Docker service.
|
* 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) {
|
function profileDockerSetup(opts, cb) {
|
||||||
assert.object(opts.cli, 'opts.cli');
|
assert.object(opts.cli, 'opts.cli');
|
||||||
assert.string(opts.name, 'opts.name');
|
assert.string(opts.name, 'opts.name');
|
||||||
assert.optionalBool(opts.implicit, 'opts.implicit');
|
assert.optionalBool(opts.implicit, 'opts.implicit');
|
||||||
assert.optionalObject(opts.keyPaths, 'opts.keyPaths');
|
|
||||||
assert.func(cb, 'cb');
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
var cli = opts.cli;
|
var cli = opts.cli;
|
||||||
@ -163,7 +159,7 @@ function profileDockerSetup(opts, cb) {
|
|||||||
|
|
||||||
vasync.pipeline({arg: {tritonapi: tritonapi}, funcs: [
|
vasync.pipeline({arg: {tritonapi: tritonapi}, funcs: [
|
||||||
function dockerKeyWarning(arg, next) {
|
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 ' +
|
'client TLS certificates that do not support encrypted ' +
|
||||||
'(passphrase protected) keys or SSH agents. If you continue,' +
|
'(passphrase protected) keys or SSH agents. If you continue,' +
|
||||||
'this profile setup will attempt to write a copy of your ' +
|
'this profile setup will attempt to write a copy of your ' +
|
||||||
@ -254,18 +250,6 @@ function profileDockerSetup(opts, cb) {
|
|||||||
next();
|
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
|
* Find the `docker` version, if we can. This can be used later to
|
||||||
* control some envvars that depend on the docker version.
|
* 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) {
|
function genClientCert_dir(arg, next) {
|
||||||
arg.dockerCertPath = path.resolve(cli.configDir,
|
arg.dockerCertPath = path.resolve(cli.configDir,
|
||||||
@ -327,7 +377,7 @@ function profileDockerSetup(opts, cb) {
|
|||||||
},
|
},
|
||||||
function genClientCert_key(arg, next) {
|
function genClientCert_key(arg, next) {
|
||||||
arg.keyPath = path.resolve(arg.dockerCertPath, 'key.pem');
|
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) {
|
fs.writeFile(arg.keyPath, data, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
next(new errors.SetupError(err, format(
|
next(new errors.SetupError(err, format(
|
||||||
@ -340,9 +390,8 @@ function profileDockerSetup(opts, cb) {
|
|||||||
function genClientCert_cert(arg, next) {
|
function genClientCert_cert(arg, next) {
|
||||||
arg.certPath = path.resolve(arg.dockerCertPath, 'cert.pem');
|
arg.certPath = path.resolve(arg.dockerCertPath, 'cert.pem');
|
||||||
|
|
||||||
var privKey = tritonapi.keyPair.getPrivateKey();
|
|
||||||
var id = sshpk.identityFromDN('CN=' + profile.account);
|
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');
|
var data = cert.toBuffer('pem');
|
||||||
|
|
||||||
fs.writeFile(arg.certPath, data, function (err) {
|
fs.writeFile(arg.certPath, data, function (err) {
|
||||||
|
Reference in New Issue
Block a user