OpenSSHは好きですか?
我々エンジニアにとって切り離せないssh。
"ssh 踏み台"というとProxyCommand
設定を書いて踏み台でsshコマンドを実行させるqiita記事が多い中、システム管理者がDMZなど特別なIPセグメントに置くサーバーにアクセスするためにネットワーク経路を迂回してアクセスことに限定した LocalForwarding
による運用について書きます。
リモートホストに対して認証可能な状態(秘密鍵とかがあること)が前提となっております。ご了承ください。
私が使用しているMac OSのOpenSSHのバージョンはこちらです。これをベースに話をします。
% ssh -V
OpenSSH_7.6p1, LibreSSL 2.6.2
forwardingの基本操作
% ssh -L 10022:リモートホスト:22 fumidai
ssh コマンドの -L
オプションを使えばローカルportをforwardingできます。
目的のリモートホストへは、ローカルportを通じて接続できるようになります。
sshのman pageには、このようにも書かれております。
-L [bind_address:]port:host:hostport
-L [bind_address:]port:remote_socket
-L local_socket:host:hostport
-L local_socket:remote_socket
Specifies that connections to the given TCP port or Unix socket on the local (client) host are to be
forwarded to the given host and port, or Unix socket, on the remote side. This works by allocating a
socket to listen to either a TCP port on the local side, optionally bound to the specified bind_address,
or to a Unix socket. Whenever a connection is made to the local port or socket, the connection is for-
warded over the secure channel, and a connection is made to either host port hostport, or the Unix
socket remote_socket, from the remote machine.
Port forwardings can also be specified in the configuration file. Only the superuser can forward privi-
leged ports. IPv6 addresses can be specified by enclosing the address in square brackets.
By default, the local port is bound in accordance with the GatewayPorts setting. However, an explicit
bind_address may be used to bind the connection to a specific address. The bind_address of
``localhost'' indicates that the listening port be bound for local use only, while an empty address or
`*' indicates that the port should be available from all interfaces.
local socketはinet socketだけでなく。unix domain socketへも同時にbindできます。
INETとUNIX domain socketのforwarding
% ssh -fN \
-L /tmp/d192.168.x.a-22.sock:192.168.x.a:22 \
-L /tmp/d192.168.x.b-22.sock:192.168.x.b:22 \
-L 10001:192.168.x.a:22 \
-L 10002:192.168.x.b:22 \
fumidai
このように1つの踏み台接続の中でINET、UNIX socketの両刀が可能です。
またforwarding先も複数指定できます。
しかし、いちいち -L
を書くのも面倒なので、ssh_configに必要なforwardを記述します。
Host fumidai
User username
Hostname 192.168.x.x
LocalForward 10001 192.168.x.A:22
LocalForward 10002 192.168.x.B:22
LocalForward /tmp/d192.168.x.C-3306.sock 192.168.x.C:3306
踏み台への接続を維持した状態でtargetホストに接続する例です。
% ssh -fN fumidai
% ssh -p 10001 -l root localhost "uname -ar"
Linux x.x.x.x 2.6.18-308.11.1.el5 #1 SMP Tue Jul 10 08:48:43 EDT 2012 x86_64 x86_64 x86_64 GNU/Linux
% cat /tmp/.my.cnf
[client]
user=ユーザー
password=パスワード
socket=/tmp/d192.168.x.C-3306.sock
% mysql --defaults-file=/tmp/.my.cnf << a
select current_timestamp;
a
setsockopt TCP_NODELAY: Operation not supported on socket
current_timestamp
2018-12-18 19:21:09
踏み台を利用して、リモートホストに接続できました。
setsockopt TCP_NODELAY: Operation not supported on socket
というwarningが出力されますが、割愛します。無視してください。
ここはあまりエレガントではないですね
forwardingの応用
リモートホストのMySQLが 127.0.0.1 からしか接続を許可していないケースを考えてみます。
たとえば
% mysql --defaults-file=~/.my.cnf
setsockopt TCP_NODELAY: Operation not supported on socket
ERROR 1045 (28000): Access denied for user 'admin'@'192.168.x.x' (using password: NO)
リモートホスト上で自分自身(localhost)に接続した場合のみ許可されているケースです。多段でforwardingします。
forward1
自分PC:tcp/10001 -> 踏み台:tcp/22 -> リモートホスト:tcp/22
forward2
自分PC:3306.sock -> forward1 -> リモートホスト:tcp/3306
ssh_configにDB接続の設定を追加します
Host fumidai
User username
Hostname 192.168.x.x
LocalForward 10001 192.168.x.A:22
Host db
User username
Hostname localhost
Port 10001
LocalForward /tmp/d192.168.x.A-3306.sock 127.0.0.1:3306
forward1
= リモートホスト:tcp/22
へのセッションに対して接続することで、リモートホストから見た時に、あたかもlocalhostから接続されたかのように接続されます。
実行例
% cat /tmp/.my.cnf
[client]
user=admin
password=パスワード
socket=/tmp/d192.168.x.A-3306.sock
% ssh -fN fumidai
% ssh -fN db
% mysql --defaults-file=/tmp/.my.cnf << a
select current_timestamp;
a
setsockopt TCP_NODELAY: Operation not supported on socket
current_timestamp
2018-12-18 19:25:12
多段forwardingにすることで、接続できました。
最後に注意事項
言うまでもないことですが、closeを忘れずに。
% ps axww | grep ssh\ -fN | grep -v grep | awk '{print $1}' | xargs kill