net-ssh-gatewayでRubyプログラム内で多段SSH+ポートフォワードを行う

2019.11.12

はじめに

こんにちは、最近は配達に忙しい佐々木です。

今回は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接続のオプションのキーproxyNet::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内で必要な前準備を完結できたので、ターミナルでポートフォワードを行うよりも手軽に実行できるスクリプトが作成できました。