mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-19 13:47:52 +00:00
Merge pull request #517 from resin-io/ssh-proxy
resin ssh proxy support
This commit is contained in:
commit
95fc5d7785
@ -10,10 +10,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Highlight cache usage in a local build
|
||||
- Show a progress bar for upload progress
|
||||
- Add ability to specify build-time variables for local builds
|
||||
- Added the proxy support for the `resin ssh` command
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed the not enough unicorns bug in resin build
|
||||
- Removed the install-time warning for the `valid-email` package
|
||||
|
||||
## [5.9.1] - 2017-05-01
|
||||
|
||||
|
@ -14,6 +14,7 @@ Requisites
|
||||
- [Git](https://git-scm.com)
|
||||
- The following executables should be correctly installed in your shell environment:
|
||||
- `ssh`: Any recent version of the OpenSSH ssh client (required by `resin sync` and `resin ssh`)
|
||||
- if you need `ssh` to work behind the proxy you also need [`proxytunnel`](http://proxytunnel.sourceforge.net/) installed (available as `proxytunnel` package for Ubuntu, for example)
|
||||
- `rsync`: >= 2.6.9 (required by `resin sync`)
|
||||
|
||||
##### Windows Support
|
||||
@ -26,6 +27,7 @@ If you still want to use `cmd.exe` you will have to use a package manager like M
|
||||
2. Install the `msys-rsync` and `msys-openssh` packages.
|
||||
3. Add MinGW to the `%PATH%` if this hasn't been done by the installer already. The location where the binaries are places is usually `C:\MinGW\msys\1.0\bin`, but it can vary if you selected a different location in the installer.
|
||||
4. Copy your SSH keys to `%homedrive%%homepath\.ssh`.
|
||||
5. If you need `ssh` to work behind the proxy you also need to install [proxytunnel](http://proxytunnel.sourceforge.net/)
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
@ -94,18 +94,4 @@ exports.pipeContainerStream = Promise.method(function(arg) {
|
||||
});
|
||||
});
|
||||
|
||||
exports.getSubShellCommand = function(command) {
|
||||
var os;
|
||||
os = require('os');
|
||||
if (os.platform() === 'win32') {
|
||||
return {
|
||||
program: 'cmd.exe',
|
||||
args: ['/s', '/c', command]
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
program: '/bin/sh',
|
||||
args: ['-c', command]
|
||||
};
|
||||
}
|
||||
};
|
||||
exports.getSubShellCommand = require('../../utils/helpers');
|
||||
|
@ -15,24 +15,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
var getSubShellCommand;
|
||||
|
||||
getSubShellCommand = function(command) {
|
||||
var os;
|
||||
os = require('os');
|
||||
if (os.platform() === 'win32') {
|
||||
return {
|
||||
program: 'cmd.exe',
|
||||
args: ['/s', '/c', command]
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
program: '/bin/sh',
|
||||
args: ['-c', command]
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
signature: 'ssh [uuid]',
|
||||
description: '(beta) get a shell into the running app container of a device',
|
||||
@ -50,18 +32,54 @@ module.exports = {
|
||||
boolean: true,
|
||||
description: 'increase verbosity',
|
||||
alias: 'v'
|
||||
}, {
|
||||
signature: 'noproxy',
|
||||
boolean: true,
|
||||
description: "don't use the proxy configuration for this connection. Only makes sense if you've configured proxy globally."
|
||||
}
|
||||
],
|
||||
action: function(params, options, done) {
|
||||
var Promise, child_process, patterns, resin, verbose;
|
||||
var Promise, _, bash, child_process, getSshProxyCommand, getSubShellCommand, hasbin, patterns, proxyConfig, resin, useProxy, verbose;
|
||||
child_process = require('child_process');
|
||||
Promise = require('bluebird');
|
||||
resin = require('resin-sdk-preconfigured');
|
||||
_ = require('lodash');
|
||||
bash = require('bash');
|
||||
hasbin = require('hasbin');
|
||||
getSubShellCommand = require('../utils/helpers').getSubShellCommand;
|
||||
patterns = require('../utils/patterns');
|
||||
if (options.port == null) {
|
||||
options.port = 22;
|
||||
}
|
||||
verbose = options.verbose ? '-vvv' : '';
|
||||
proxyConfig = global.PROXY_CONFIG;
|
||||
useProxy = !!proxyConfig && !options.noproxy;
|
||||
getSshProxyCommand = function(hasTunnelBin) {
|
||||
var i, proxyAuth, proxyCommand, tunnelOptions;
|
||||
if (!useProxy) {
|
||||
return '';
|
||||
}
|
||||
if (!hasTunnelBin) {
|
||||
console.warn('Proxy is enabled but the `proxytunnel` binary cannot be found.\nPlease install it if you want to route the `resin ssh` requests through the proxy.\nAlternatively you can pass `--noproxy` param to the `resin ssh` command to ignore the proxy config\nfor the `ssh` requests.\n\nAttemmpting the unproxied request for now.');
|
||||
return '';
|
||||
}
|
||||
tunnelOptions = {
|
||||
proxy: proxyConfig.host + ":" + proxyConfig.port,
|
||||
dest: '%h:%p'
|
||||
};
|
||||
proxyAuth = proxyConfig.proxyAuth;
|
||||
if (proxyAuth) {
|
||||
i = proxyAuth.indexOf(':');
|
||||
_.assign(tunnelOptions, {
|
||||
user: proxyAuth.substring(0, i),
|
||||
pass: proxyAuth.substring(i + 1)
|
||||
});
|
||||
}
|
||||
proxyCommand = "proxytunnel " + (bash.args(tunnelOptions, '--', '='));
|
||||
return "-o " + (bash.args({
|
||||
ProxyCommand: proxyCommand
|
||||
}, '', '='));
|
||||
};
|
||||
return Promise["try"](function() {
|
||||
if (!params.uuid) {
|
||||
return false;
|
||||
@ -73,7 +91,7 @@ module.exports = {
|
||||
}
|
||||
return patterns.inferOrSelectDevice();
|
||||
}).then(function(uuid) {
|
||||
console.info("Connecting with: " + uuid);
|
||||
console.info("Connecting to: " + uuid);
|
||||
return resin.models.device.get(uuid);
|
||||
}).then(function(device) {
|
||||
if (!device.is_online) {
|
||||
@ -83,16 +101,18 @@ module.exports = {
|
||||
username: resin.auth.whoami(),
|
||||
uuid: device.uuid,
|
||||
containerId: resin.models.device.getApplicationInfo(device.uuid).get('containerId'),
|
||||
proxyUrl: resin.settings.get('proxyUrl')
|
||||
proxyUrl: resin.settings.get('proxyUrl'),
|
||||
hasTunnelBin: useProxy ? hasbin('proxytunnel') : null
|
||||
}).then(function(arg) {
|
||||
var containerId, proxyUrl, username, uuid;
|
||||
username = arg.username, uuid = arg.uuid, containerId = arg.containerId, proxyUrl = arg.proxyUrl;
|
||||
var containerId, hasTunnelBin, proxyUrl, username, uuid;
|
||||
username = arg.username, uuid = arg.uuid, containerId = arg.containerId, proxyUrl = arg.proxyUrl, hasTunnelBin = arg.hasTunnelBin;
|
||||
if (containerId == null) {
|
||||
throw new Error('Did not find running application container');
|
||||
}
|
||||
return Promise["try"](function() {
|
||||
var command, subShellCommand;
|
||||
command = "ssh " + verbose + " -t -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ControlMaster=no -p " + options.port + " " + username + "@ssh." + proxyUrl + " enter " + uuid + " " + containerId;
|
||||
var command, sshProxyCommand, subShellCommand;
|
||||
sshProxyCommand = getSshProxyCommand(hasTunnelBin);
|
||||
command = "ssh " + verbose + " -t -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ControlMaster=no " + sshProxyCommand + " -p " + options.port + " " + username + "@ssh." + proxyUrl + " enter " + uuid + " " + containerId;
|
||||
subShellCommand = getSubShellCommand(command);
|
||||
return child_process.spawn(subShellCommand.program, subShellCommand.args, {
|
||||
stdio: 'inherit'
|
||||
|
@ -47,6 +47,8 @@ try {
|
||||
|
||||
globalTunnel.initialize(proxy);
|
||||
|
||||
global.PROXY_CONFIG = globalTunnel.proxyConfig;
|
||||
|
||||
_ = require('lodash');
|
||||
|
||||
Promise = require('bluebird');
|
||||
|
@ -110,3 +110,19 @@ exports.getAppInfo = function(application) {
|
||||
return app;
|
||||
});
|
||||
};
|
||||
|
||||
exports.getSubShellCommand = function(command) {
|
||||
var os;
|
||||
os = require('os');
|
||||
if (os.platform() === 'win32') {
|
||||
return {
|
||||
program: 'cmd.exe',
|
||||
args: ['/s', '/c', command]
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
program: '/bin/sh',
|
||||
args: ['-c', command]
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||
*/
|
||||
var validEmail;
|
||||
|
||||
validEmail = require('valid-email');
|
||||
validEmail = require('@resin.io/valid-email');
|
||||
|
||||
exports.validateEmail = function(input) {
|
||||
if (!validEmail(input)) {
|
||||
|
@ -786,6 +786,10 @@ ssh gateway port
|
||||
|
||||
increase verbosity
|
||||
|
||||
#### --noproxy
|
||||
|
||||
don't use the proxy configuration for this connection. Only makes sense if you've configured proxy globally.
|
||||
|
||||
# Notes
|
||||
|
||||
## note <|note>
|
||||
|
@ -58,21 +58,4 @@ exports.pipeContainerStream = Promise.method ({ deviceIp, name, outStream, follo
|
||||
return console.log(chalk.red.bold("Container '#{name}' not found."))
|
||||
throw err
|
||||
|
||||
# A function to reliably execute a command
|
||||
# in all supported operating systems, including
|
||||
# different Windows environments like `cmd.exe`
|
||||
# and `Cygwin` should be encapsulated in a
|
||||
# re-usable package.
|
||||
exports.getSubShellCommand = (command) ->
|
||||
os = require('os')
|
||||
|
||||
if os.platform() is 'win32'
|
||||
return {
|
||||
program: 'cmd.exe'
|
||||
args: [ '/s', '/c', command ]
|
||||
}
|
||||
else
|
||||
return {
|
||||
program: '/bin/sh'
|
||||
args: [ '-c', command ]
|
||||
}
|
||||
exports.getSubShellCommand = require('../../utils/helpers')
|
||||
|
@ -14,27 +14,6 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
###
|
||||
|
||||
# TODO: A function to reliably execute a command
|
||||
# in all supported operating systems, including
|
||||
# different Windows environments like `cmd.exe`
|
||||
# and `Cygwin` should be encapsulated in a
|
||||
# re-usable package.
|
||||
# This is literally copy-pasted from the `resin-sync`
|
||||
# module.
|
||||
getSubShellCommand = (command) ->
|
||||
os = require('os')
|
||||
|
||||
if os.platform() is 'win32'
|
||||
return {
|
||||
program: 'cmd.exe'
|
||||
args: [ '/s', '/c', command ]
|
||||
}
|
||||
else
|
||||
return {
|
||||
program: '/bin/sh'
|
||||
args: [ '-c', command ]
|
||||
}
|
||||
|
||||
module.exports =
|
||||
signature: 'ssh [uuid]'
|
||||
description: '(beta) get a shell into the running app container of a device'
|
||||
@ -65,18 +44,55 @@ module.exports =
|
||||
boolean: true
|
||||
description: 'increase verbosity'
|
||||
alias: 'v'
|
||||
,
|
||||
signature: 'noproxy'
|
||||
boolean: true
|
||||
description: "don't use the proxy configuration for this connection.
|
||||
Only makes sense if you've configured proxy globally."
|
||||
]
|
||||
action: (params, options, done) ->
|
||||
child_process = require('child_process')
|
||||
Promise = require 'bluebird'
|
||||
Promise = require('bluebird')
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
_ = require('lodash')
|
||||
bash = require('bash')
|
||||
hasbin = require('hasbin')
|
||||
{ getSubShellCommand } = require('../utils/helpers')
|
||||
patterns = require('../utils/patterns')
|
||||
|
||||
if not options.port?
|
||||
options.port = 22
|
||||
options.port ?= 22
|
||||
|
||||
verbose = if options.verbose then '-vvv' else ''
|
||||
|
||||
proxyConfig = global.PROXY_CONFIG
|
||||
useProxy = !!proxyConfig and not options.noproxy
|
||||
|
||||
getSshProxyCommand = (hasTunnelBin) ->
|
||||
return '' if not useProxy
|
||||
|
||||
if not hasTunnelBin
|
||||
console.warn('''
|
||||
Proxy is enabled but the `proxytunnel` binary cannot be found.
|
||||
Please install it if you want to route the `resin ssh` requests through the proxy.
|
||||
Alternatively you can pass `--noproxy` param to the `resin ssh` command to ignore the proxy config
|
||||
for the `ssh` requests.
|
||||
|
||||
Attemmpting the unproxied request for now.
|
||||
''')
|
||||
return ''
|
||||
|
||||
tunnelOptions =
|
||||
proxy: "#{proxyConfig.host}:#{proxyConfig.port}"
|
||||
dest: '%h:%p'
|
||||
{ proxyAuth } = proxyConfig
|
||||
if proxyAuth
|
||||
i = proxyAuth.indexOf(':')
|
||||
_.assign tunnelOptions,
|
||||
user: proxyAuth.substring(0, i)
|
||||
pass: proxyAuth.substring(i + 1)
|
||||
proxyCommand = "proxytunnel #{bash.args(tunnelOptions, '--', '=')}"
|
||||
return "-o #{bash.args({ ProxyCommand: proxyCommand }, '', '=')}"
|
||||
|
||||
Promise.try ->
|
||||
return false if not params.uuid
|
||||
return resin.models.device.has(params.uuid)
|
||||
@ -84,7 +100,7 @@ module.exports =
|
||||
return params.uuid if uuidExists
|
||||
return patterns.inferOrSelectDevice()
|
||||
.then (uuid) ->
|
||||
console.info("Connecting with: #{uuid}")
|
||||
console.info("Connecting to: #{uuid}")
|
||||
resin.models.device.get(uuid)
|
||||
.then (device) ->
|
||||
throw new Error('Device is not online') if not device.is_online
|
||||
@ -95,14 +111,18 @@ module.exports =
|
||||
# get full uuid
|
||||
containerId: resin.models.device.getApplicationInfo(device.uuid).get('containerId')
|
||||
proxyUrl: resin.settings.get('proxyUrl')
|
||||
.then ({ username, uuid, containerId, proxyUrl }) ->
|
||||
|
||||
hasTunnelBin: if useProxy then hasbin('proxytunnel') else null
|
||||
.then ({ username, uuid, containerId, proxyUrl, hasTunnelBin }) ->
|
||||
throw new Error('Did not find running application container') if not containerId?
|
||||
Promise.try ->
|
||||
sshProxyCommand = getSshProxyCommand(hasTunnelBin)
|
||||
command = "ssh #{verbose} -t \
|
||||
-o LogLevel=ERROR \
|
||||
-o StrictHostKeyChecking=no \
|
||||
-o UserKnownHostsFile=/dev/null \
|
||||
-o ControlMaster=no \
|
||||
#{sshProxyCommand} \
|
||||
-p #{options.port} #{username}@ssh.#{proxyUrl} enter #{uuid} #{containerId}"
|
||||
|
||||
subShellCommand = getSubShellCommand(command)
|
||||
|
@ -38,6 +38,9 @@ catch
|
||||
# If that is not set as well the initialize will do nothing
|
||||
globalTunnel.initialize(proxy)
|
||||
|
||||
# TODO: make this a feature of capitano https://github.com/resin-io/capitano/issues/48
|
||||
global.PROXY_CONFIG = globalTunnel.proxyConfig
|
||||
|
||||
_ = require('lodash')
|
||||
Promise = require('bluebird')
|
||||
capitano = require('capitano')
|
||||
|
@ -105,3 +105,21 @@ exports.getAppInfo = (application) ->
|
||||
app.arch = config.arch
|
||||
return app
|
||||
)
|
||||
|
||||
# A function to reliably execute a command
|
||||
# in all supported operating systems, including
|
||||
# different Windows environments like `cmd.exe`
|
||||
# and `Cygwin`.
|
||||
exports.getSubShellCommand = (command) ->
|
||||
os = require('os')
|
||||
|
||||
if os.platform() is 'win32'
|
||||
return {
|
||||
program: 'cmd.exe'
|
||||
args: [ '/s', '/c', command ]
|
||||
}
|
||||
else
|
||||
return {
|
||||
program: '/bin/sh'
|
||||
args: [ '-c', command ]
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
###
|
||||
|
||||
validEmail = require('valid-email')
|
||||
validEmail = require('@resin.io/valid-email')
|
||||
|
||||
exports.validateEmail = (input) ->
|
||||
if not validEmail(input)
|
||||
|
@ -33,10 +33,12 @@
|
||||
"gulp-shell": "^0.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@resin.io/valid-email": "^0.1.0",
|
||||
"ansi-escapes": "^2.0.0",
|
||||
"any-promise": "^1.3.0",
|
||||
"babel-preset-es2015": "^6.16.0",
|
||||
"babel-register": "^6.16.3",
|
||||
"bash": "0.0.1",
|
||||
"bluebird": "^3.3.3",
|
||||
"capitano": "^1.7.0",
|
||||
"chalk": "^1.1.3",
|
||||
@ -47,7 +49,8 @@
|
||||
"dockerode": "^2.4.2",
|
||||
"drivelist": "^5.0.16",
|
||||
"etcher-image-write": "^9.0.3",
|
||||
"global-tunnel-ng": "^2.0.0",
|
||||
"global-tunnel-ng": "^2.1.0",
|
||||
"hasbin": "^1.2.3",
|
||||
"inquirer": "^3.0.6",
|
||||
"is-root": "^1.0.0",
|
||||
"js-yaml": "^3.7.0",
|
||||
@ -85,8 +88,7 @@
|
||||
"umount": "^1.1.5",
|
||||
"underscore.string": "^3.1.1",
|
||||
"unzip2": "^0.2.5",
|
||||
"update-notifier": "^0.6.1",
|
||||
"valid-email": "^0.0.2"
|
||||
"update-notifier": "^0.6.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"removedrive": "^1.0.0"
|
||||
|
Loading…
Reference in New Issue
Block a user