web-dev-qa-db-ja.com

内部で機能しているにもかかわらず、AWS API GatewayからForbiddenメッセージが表示されるのはなぜですか?

認証のないパブリックエンドポイント用にAWS APIゲートウェイをセットアップしています。 LambdaをトリガーするWebSocketに接続します。

https://pypi.org/project/websocket_client/ でPythonのwebsocket-client libとの接続を作成していました。

接続が約10%の時間失敗し、負荷が増加すると悪化することに気づきました。私の一般的なAPIゲートウェイ設定でYour current account level throttling rate is 10000 requests per second with a burst of 5000 requests.と表示されているので、スロットルが表示されるはずの場所が見つかりません。これは、1秒あたりわずか2〜3件のリクエストで問題がかなり頻繁にトリガーされるという点とは異なります。

一方、失敗の応答は{u'message': u'Forbidden', u'connectionId': u'Z2Jp-dR5vHcCJkg=', u'requestId': u'Z2JqAEJRvHcFzvg='}のようになります

CloudWatchログの洞察を調べ、接続IDとリクエストIDを検索しました。 APIゲートウェイのロググループでは、どちらのIDでも結果が見つかりません。それでも、websocket接続で起動する私のLambdaを検索すると、その接続IDのログが表示されます。ログには、すべてが正常に実行されていることが示されています。ラムダは、起動するMySQLクエリを実行するだけです。

ラムダが期待どおりに動作しているにもかかわらず、なぜ禁止の応答が返されるのですか?

(getting message:forbidden reply from AWS API gateway )にある既存の質問は、一部のプライベートエンドポイントに対して常に禁止されている場合に対処するようです。私のユースケースに沿ったものは何もありません。

[〜#〜]更新[〜#〜]

これはlocust.io、または毎秒接続するために使用しているpythonに関連していると思います。私は https://www.npmjs.com/package/wscat をマシンにインストールし、接続とクローズをできるだけ速く繰り返しています。 Forbiddenメッセージが表示されません。接続方法がForbiddenメッセージをランダムに吐き出す方法がわからないので、混乱するだけです

class SocketClient(object):
    def __init__(self, Host):
        self.Host = Host
        self.session_id = uuid4().hex

    def connect(self):
        self.ws = websocket.WebSocket()
        self.ws.settimeout(10)
        self.ws.connect(self.Host)

        events.quitting += self.on_close

        data = self.attach_session({})
        return data

    def attach_session(self, payload):
        message_id = uuid4().hex
        start_time = time.time()
        e = None
        try:
            print("Sending payload {}".format(payload))
            data = self.send_with_response(payload)
            assert data['mykey']

        except AssertionError as exp:
            e = exp
        except Exception as exp:
            e = exp
            self.ws.close()
            self.connect()
        elapsed = int((time.time() - start_time) * 1000)
        if e:
            events.request_failure.fire(request_type='sockjs', name='send',
                                        response_time=elapsed, exception=e)
        else:
            events.request_success.fire(request_type='sockjs', name='send',
                                        response_time=elapsed,
                                        response_length=0)
        return data

    def send_with_response(self, payload):
        json_data = json.dumps(payload)

        g = gevent.spawn(self.ws.send, json_data)
        g.get(block=True, timeout=2)
        g = gevent.spawn(self.ws.recv)
        result = g.get(block=True, timeout=10)

        json_data = json.loads(result)
        return json_data
    def on_close(self):
        self.ws.close()

class ActionsTaskSet(TaskSet):
    @task
    def streams(self):
        response = self.client.connect()
        logger.info("Connect Response: {}".format(response))

class WSUser(Locust):
    task_set = ActionsTaskSet
    min_wait = 1000
    max_wait = 3000

    def __init__(self, *args, **kwargs):
        super(WSUser, self).__init__(*args, **kwargs)
        self.client = SocketClient('wss://mydomain.amazonaws.com/endpoint')

enter image description here

更新2

以前は存在しなかった1種類のログであるアクセスログを有効にしました。私のラムダが問題なく常に200を取得していることがわかります。 403は、実際のMESSAGEにヒットしないeventTyperouteKeyからのものです。それがどこから来るのかはわかりませんが、その答えを見つけることでこれを解決できます。

ENIの問題がないことも確認できました。

enter image description here

14
Dave Stein

私の例のペイロードは空です。 APIは、routeKeyを認識するために$request.body.actionを使用するように構成されています。接続すると、デフォルトの$connectルートが機能します。

私の体に適切なactionを追加すると、403はなくなりました。これが解決策です。基本的に、接続と切断のアクションから200の応答を受け取りましたが、ペイロードのないメッセージが通過するたびに403を受け取りました。

0
Dave Stein

VPC関連の制限に達している可能性があります。 https://winterwindsoftware.com/scaling-lambdas-inside-vpc/ を参照してください。 ENIが不足しているようです。関数を別のVPCに移動してみてください。ラムダの各呼び出しにはどのくらいの時間がかかりますか?そして、ラムダ語で書かれた言語は何ですか?

4
complex