We're using Capistrano to automate pushing new versions of a PHP application to a production server. The production server (we'll call it production) is public, while our repository server (we'll call it repo) sits behind our corporate firewall, along with our own machines.
Capistrano, as configured by default, won't work, as production can't talk to repo.
I was wondering if there was someway I could setup capistrano to SSH to repo first, then SSH to production opening a tunnel on a port that I can then use to SSH from production back to repo to pull the changes from SCM.
I just can't figure out how to set this up or figure out a better solution. Ideas?
Edit:
I've tried this:
role :web, "deploy.com"
namespace :deploy do
task :remote_tunnel do
run 'Creating SSH tunnel...' do |channel, stream, data|
ssh = channel.connection
ssh.forward.remote(22, 'server.com', 10000, '127.0.0.1')
ssh.loop {!ssh.forward.active_remotes.include?([10000, '127.0.0.1'])}
end
end
end
before "deploy:update_code", "deploy:remote_tunnel"
But I keep getting this error:
failed: "sh -c 'Creating SSH tunnel...'" on deploy.com
Here's are 2 ways to accomplish it.
1st way
not sure if you've seen this thread?
It makes use of the net-ssh-gateway
library, but creates copies of the local forwarding methods but they're geared for remote access.
class Net::SSH::Gateway
# Opens a SSH tunnel from a port on a remote host to a given host and port
# on the local side
# (equivalent to openssh -R parameter)
def open_remote(port, host, remote_port, remote_host = "127.0.0.1")
ensure_open!
@session_mutex.synchronize do
@session.forward.remote(port, host, remote_port, remote_host)
end
if block_given?
begin
yield [remote_port, remote_host]
ensure
close_remote(remote_port, remote_host)
end
else
return [remote_port, remote_host]
end
rescue Errno::EADDRINUSE
retry
end
# Cancels port-forwarding over an open port that was previously opened via
# open_remote.
def close_remote(port, host = "127.0.0.1")
ensure_open!
@session_mutex.synchronize do
@session.forward.cancel_remote(port, host)
end
end
end
2nd way
Outlined in an answer to this SO question:
This technique is very similar to the 1st way. First you need to create 2 paths to the repository:
# deploy.rb
set :local_repository, "ssh://git@serverbehindfirewall/path/to/project.git"
set :repository, "ssh://git@localhost:9000/path/to/project.git"
Then before you deploy you'll need to setup the remote forward:
% ssh -R 9000:serverbehindfirewall:22 deploybot@deployserver.com
# CTRL + C + A (Screen) or ⌘ + T (Terminal.app) to open new tab
Followed by your deploy:
% cap HOSTFILTER=deployserver.com deploy # HOSTFILTER reduces set to specified host. Only useful if you have multiple servers.
See this answer to that SO question for more details:
Using Capistrano 3.x, the following works for me:
namespace :deploy do
desc "Open SSH Tunnel to GitLab"
task :open_tunnel do
on roles(:app) do
info "Opening SSH Remote Tunnel..."
self.send(:with_ssh) do |ssh|
# ssh -R 9000:192.168.1.123:22
ssh.forward.remote(22, "192.168.1.123", 9000)
end
end
end
before "deploy:check", "deploy:open_tunnel"
end
Please note that ssh.forward.remote
expects parameters in a different order than ssh -R
, the above is equivalent to ssh -R 9000:192.168.1.123:22
This task calls a private method, if anyone knows an official way to get the access Capistrano's ssh connection, please comment or edit.
Edit: Also see the section Tunneling and other related SSH themes of SSHKit's README
来源:https://stackoverflow.com/questions/14278822/capistrano-firewalls-and-tunnel