この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
こんにちは、最近は配達に忙しい佐々木です。
今回はVPC内のプライベートサブネットに存在するRedisへ接続してデータ操作を行うRubyスクリプトを作りたくてnet-ssh-gateway というgemを試してみたので、そのメモです。
やりたいこと
下記の図のように、VPC内のプライベートサブネットに存在するRedisにローカルのMacから接続します。 Redisにはプライベートサブネットからのみ接続できず、app インスタンスにはbastionインスタンスを経由しないと接続できません。
そこで次のような経路で接続を行います。
- appインスタンスへbastion経由の多段SSHへ接続します
- RedisへはSSHポートフォワードでMacのローカルポートを転送します
net-ssh-gateway
Rubyのプログラム内でSSHポートフォワードを行うにはnet-ssh-gateway が利用できます。 ポートフォワードでRedisへ接続する(多段SSHはなし)場合は下記のようになります。
require 'net/ssh/gateway'
require 'redis'
# SSH接続するインスタンスの情報
GW_USER = 'ec2-user'
GW_HOST = 'app'
SSH_OPT = {
keys: ['~/.ssh/my_key.pem'],
}
# Redisの情報
REDIS_HOST = 'redis'
REDIS_PORT = 6379
gateway = Net::SSH::Gateway.new(
GW_HOST,
GW_USER,
SSH_OPT
)
gateway.open(REDIS_HOST, REDIS_PORT) do |forwared_port|
# フォワードされたローカルのポートへ接続する
redis = Redis.new(host: '127.0.0.1', port: forwared_port, db: db)
begin
p redis.ping # PONG
ensure
redis.quit
end
end
多段SSHでポートフォワード
冒頭に書いた通り今回は多段SSHが必要です。多段SSHを行うにはNet::SSH::Proxy::Commandを使用します。
下記のようにSSH接続のオプションのキーproxy
にNet::SSH::Proxy::Command
のインスタンスを指定します。
Net::SSH::Proxy::Command.new
の引数は~/.ssh/config
などに記述するProxyCommand
と同じものでオッケーです。
require 'net/ssh/gateway'
require 'redis'
require 'net/ssh/proxy/command'
# SSH接続するインスタンスの情報
GW_USER = 'ec2-user'
GW_HOST = 'app'
SSH_OPT = {
keys: ['~/.ssh/my_key.pem'],
proxy: Net::SSH::Proxy::Command.new('ssh -i ~/.ssh/my_key.pem -W %h:%p ec2-user@bastion')
}
# Redisの情報
REDIS_HOST = 'redis'
REDIS_PORT = 6379
gateway = Net::SSH::Gateway.new(
GW_HOST,
GW_USER,
SSH_OPT
)
gateway.open(REDIS_HOST, REDIS_PORT) do |forwared_port|
redis = Redis.new(host: '127.0.0.1', port: forwared_port, db: db)
begin
p redis.ping # PONG
ensure
redis.quit
end
end
注意点
リポジトリのREADMEに書かれている通り、net-ssh-gatewayはアクティブな開発が行われていません。
Please note: this project is in maintenance mode. It is not under active development but pull requests are very much welcome. Just be sure to include tests!
プロダクションコードで利用する際にはその点に留意が必要です。
まとめ
net-ssh-gatewayを使ってRubyでSSHポートフォワードと多段SSHを経由したRedis接続が行えました。 Ruby内で必要な前準備を完結できたので、ターミナルでポートフォワードを行うよりも手軽に実行できるスクリプトが作成できました。