5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

マネージド・サーバーレス・踏み台サーバーのススメ

Posted at

※ ネタです。

AWSのVPCでプライベートなサブネットにおいてあるEC2にSSHでアクセスしたいとき、
踏み台サーバーを作成して、多段でアクセスする方法があります。

ただ、金やセキュリティのために踏み台EC2を削除をするので、SSHする度に作ったり落としたりするのもめんどくさいです。
そんなことを考えながら、ネット記事を見ていたらGoでaws lambdaにアクセスする方法が書いてありました。

SSH-ing into your AWS Lambda Functions
https://medium.com/clog/ssh-ing-into-your-aws-lambda-functions-c940cebf7646

SSHできるんだったら、トンネル開けてやれば5分間踏み台サーバーとして利用できそうです。

記事ではGoだったので、なんとなくpython(3.6)で。

考えること

(1)トンネル掘りのため、sshができるparamikoが必要
(2)lambdaはセキュリティグループで設定してもインバウンドのポート閉じちゃってるらしい

(1)については、lambda用にzipを作るときにpip install -t paramiko . でOK。
(2)については、lambdaからSSHでアクセスしてもらい、逆ポートフォワーディングでトンネルを作成。

準備

  1. VPCでプライベートなEC2インスタンス
  2. プライベートなサブネット2個以上。(VPCにlambda設置のため)
  3. サブネットにNATゲートウェイ(プライベートサブネット内のlambdaが外と通信するため)
  4. NAT越えのためngrok(手元のPCとlambdaをつなぎたいので)

AWS Lambdaのコード

paramikoのところにデモがあったのでそれを流用。

import os
import socket
import select
import sys
import threading
import paramiko

def handler(chan, host, port):
    sock = socket.socket()
    try:
        sock.connect((host, port))
    except Exception as e:
        print('Forwarding request to %s:%d failed: %r' % (host, port, e))
        return None

    print('Connected!  Tunnel open %r -> %r -> %r' %(chan.origin_addr, chan.getpeername(), (host, port)))

    while True:
        r, _w, _x = select.select([sock, chan], [], [])
        if sock in r:
            data = sock.recv(1024)
            if len(data) == 0:
                break
            chan.send(data)
        if chan in r:
            data = chan.recv(1024)
            if len(data) == 0:
                break
            sock.send(data)

    chan.close()
    sock.close()

    return None

def reverse_forward_tunnel(server_port, remote_host, remote_port, transport):
    transport.request_port_forward('', server_port)
    while True:
        chan = transport.accept(1000)
        if chan is None:
            continue
        thr = threading.Thread(target=handler, args=(chan, remote_host, remote_port))
        thr.setDaemon(True)
        thr.start()

def main_handler(event, context):
    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    if os.environ.get('CIRCUIT_BREAKER') is not None: # 再起動用
        print("canncel handler.")
        return None

    try:
        host = os.environ.get('LOCAL_HOST', '0.tcp.ap.ngrok.io')
        port = os.environ['LOCAL_PORT']
        username = os.environ['LOCAL_USERNAME']
        password = os.environ['LOCAL_PASSWORD']

        reverse_port = os.environ.get('REVERSE_PORT', 9999)
        remote_host = os.environ['REMOTE_HOST']
        remote_port = os.environ.get('REMOTE_PORT', 22)

        client.connect(host, int(port), username = username, password = password)

        print('Create tunnel %s:%s:%s' % (reverse_port, remote_host, remote_port))
        reverse_forward_tunnel(int(reverse_port), remote_host, int(remote_port), client.get_transport())
    except Exception as e:
        print('Connect failed: %r' % e)

    return None

if __name__ == '__main__':
    from os.path import join, dirname
    from dotenv import load_dotenv

    dotenv_path = join(dirname(__file__), '.env')
    load_dotenv(dotenv_path)

    main_handler({}, {})

実行

  1. ↑のソースコードをparamiko付きでプライベートなサブネット内lambdaに設置
  2. lambdaの環境変数を設定
  3. 手元のPCにngrokをダウンロード&認証をしてやる
  4. ngrokをtcp 22で起動(ngrok tcp 22 --region ap)。
    ngrokのトンネル用ポートが表示されるので、lambdaの環境変数に設定(LOCAL_PORT)
  5. lambda起動
  6. うまく起動できたら、ngrokを起動しているPCにトンネルが接続されているので
    SSHでプライベートなEC2にアクセス

SSHコマンドだと、こんな感じになっているかと。

# lambda内
$ ssh -R <REVERSE_PORT>:<REMOTE_HOST>:<REMOTE_PORT> <LOCAL_USERNAME>@<LOCAL_HOST> -p <LOCAL_PORT>

# 手元のPCなど
$ ssh 127.0.0.1 -p <REVERSE_PORT> -l ec2-user -i ~/.ssh/ec2-keypair.pem

感想

当たり前ですが、踏み台lambdaが落ちれば接続も切れますので5分しか使えません。
なので、5分間で作業を終わらしてください。(接続先EC2内にtmuxとか起動していれば……)

また、Goは標準でssh用のやつ(x/crypto/ssh)を持っているので、Goの方が楽そうです。

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?