インフラ・DevOps
WireGuardオープンエンドポイント
Open WireGuard Endpoints (proxylity.com)
要約
UDP Gatewayは、WireGuardリスナーが事前登録なしで接続を受け入れる機能と、Lambda宛先を非同期で呼び出す機能を追加しました。これにより、従来はカスタムインフラを必要とした、パブリックfacingでイベント駆動型のWireGuardサービスが構築可能になります。オープンエンドポイントはHTTPSと同様のモデルで、共有プリシェアードキー(PSK)による認証ゲートも提供可能です。
全文翻訳
本日、UDP Gatewayで2つの新しい機能が利用可能になりました。1つ目は、WireGuardリスナーが事前登録なしでクライアントからの接続を受け入れられるようにする機能で、これはHTTPSがウェブサイトに使用するのと同じモデルです。2つ目は、Lambda宛先を非同期で呼び出せるようになり、ゲートウェイが応答を待たずに長時間実行されるワークフローにパケットを送信できるようになります。これら2つを組み合わせることで、従来は構築にかなりのカスタムインフラを必要とした、パブリックfacingでイベント駆動型のWireGuardサービスのクラスが開かれます。
WireGuardオープンエンドポイント
WireGuardリスナーは、常にすべての接続クライアントが事前登録されている必要がありました。CloudFormationテンプレートに各ピアの公開鍵をリストし、リスナーは未知の鍵からのハンドシェイクを拒否します。このモデルは、管理されたデバイスセットを持つプライベートサービスに適しています。しかし、これは、大規模または未知のクライアントセットからアクセス可能にする必要があるものにとっては問題となります。最初の起動時にWireGuardキーペアを生成するモバイルアプリを考えてみてください。または、オンデマンドでプロビジョニングされるデバイスのフリートで、中央のキー管理が運用上非現実的な場合。または、これまで見たことのないクライアントが接続する必要がある、パブリックfacingのサービス。
以前のモデルでは、これらの各クライアントは、ハンドシェイクを完了する前に、帯域外登録ステップとCloudFormationの更新が必要でした。これはパブリックサービスにはスケーリングしないモデルです。
新しいAllowUnknownPeersプロパティは、その制限を削除します。WireGuardリスナーでこれをtrueに設定すると、ゲートウェイは、公開鍵がリストされているかどうかにかかわらず、有効なWireGuardクライアントとのハンドシェイクを完了します。接続は依然として完全に暗号化されています。WireGuardの暗号化プロパティは変更されません。違いは、リスナーが事前に鍵を知っている必要がなくなったということです。
HTTPSへのアナロジーは意図的です。HTTPS経由でウェブサイトにアクセスする場合、サーバーは暗号化された接続を確立する前に、あなたが誰であるかを知る必要はありません。TLSハンドシェイクが完了し、チャネルが暗号化され、認証(もし行われる場合)はアプリケーションレイヤーでの別の懸念事項です。オープンWireGuardエンドポイントも同様に機能します。トランスポートは暗号化され、IDはアプリケーションが必要とする方法でLambdaまたはStep Functions宛先によって処理できます。
共有資格情報ゲートの追加
完全にオープンな登録(文字通り任意のWireGuardクライアントを受け入れる)は、一部のサービスに適しています。他のサービスでは、暗号化とオープンハンドシェイクが必要ですが、資格情報が発行されていないクライアントからの接続を防ぎたい場合もあります。UnknownPeerPreSharedKeyプロパティは、まさにこのケースのための軽量ゲートを提供します。UnknownPeerPreSharedKeyが設定されている場合、未知のピアはWireGuard構成にそのPSKを含める必要があります。そうしないとハンドシェイクが失敗します。これはデバイスごとの認証ではありません。すべてのクライアントが同じ秘密を使用しますが、PSKが発行されたクライアントからのアクセスを実質的に制限します。トランスポートレイヤーアクセス用の共有APIキーのようなものと考えてください。強力なIDではありませんが、資格情報が発行されたことのないクライアントからの任意の接続に対する実際の障壁です。PSKをクライアントに配布することは、アプリケーションの責任です。AWS Secrets Managerに保存し、インフラストラクチャ側ではCloudFormationスタックから参照してください。クライアント側では、製造中または登録中にデバイスにプロビジョニングしてください。
```yaml
WireGuardListener:
Type: Custom::ProxylityUdpGatewayListener
Properties:
ServiceToken: !FindInMap [ProxylityConfig, !Ref "AWS::Region", ServiceToken]
ApiKey: !FindInMap [ProxylityConfig, Account, ApiKey]
Protocols:
- wg
AllowUnknownPeers: true
UnknownPeerPreSharedKey: !Sub "{{resolve:secretsmanager:${WireGuardPSK}:SecretString}}"
Destinations:
- Name: packet-handler
DestinationArn: !GetAtt HandlerLambda.Arn
Role: Arn: !GetAtt ProxylityRole.Arn
```
名前付きピア(Peers配列にリストされている)は、AllowUnknownPeersおよびUnknownPeerPreSharedKeyの影響を受けません。それらは以前と同様に、独自のピアごとのSharedSecretを使用します。2つのモデルは、単一のリスナーで共存できます。ピアごとのPSKを持つ既知のデバイスの固定セットと、共有資格情報の背後にある動的クライアント用のオープンなスロットです。
Lambda非同期呼び出し
UDP GatewayのLambda宛先は、常に同期的なRequestResponse呼び出しを使用していました。ゲートウェイはパケットのバッチを配信し、関数が完了するのを待ち、関数の戻り値を使用してクライアントに返信パケットを送信します。このモデルは、UDPリクエスト/レスポンスパターンがどのように機能するかと一致するようにデフォルトになっています。しかし、同期呼び出しは、関数のジョブが単一の呼び出しを超えて処理を開始することであるワークロードにとって制限的になります。Lambdaの永続関数はまさにこれを扱います。チェックポイントとリプレイメカニズムを使用して、永続関数は最大1年間実行でき、進捗を失うことなく障害後に自動的に再開します。
Event呼び出しタイプは、ここで適切な配信モデルです。ゲートウェイはパケットバッチを配信し、永続実行が独立して継続する間に先に進みます。新しいUseAsyncInvoke引数は、呼び出しタイプをLambdaのEventモードに変更します。ゲートウェイはパケットバッチを配信し、関数が完了するのを待たずにすぐにHTTP 202 Acceptedを受信します。UDPクライアントへの応答は送信されません。関数はゲートウェイのリクエストライフサイクルとは無関係に完了まで実行されます。
主なユースケースは、永続的なワークロードをトリガーすることです。UseAsyncInvokeで呼び出された関数は、受信したパケットバッチを受け取り、永続的な実行を開始します(または、オーケストレーションモデルを好む場合はStep Functionsにディスパッチします)、すぐに返します。リモートクライアントはUDPパケットを送信しました。チェックポイントされた進捗と障害からの自動回復を備えた、長時間実行される耐障害性のあるワークフローが、ゲートウェイが接続を開いたままにしておく必要なしに、そのクライアントのために現在実行されています。
```yaml
Destinations:
- Name: workflow-trigger
DestinationArn: !GetAtt WorkflowTriggerLambda.Arn
Role: Arn: !GetAtt ProxylityRole.Arn
Arguments:
UseAsyncInvoke: "true"
```
非同期呼び出しを使用する際に留意すべき点がいくつかあります。
* 応答なし:関数の戻り値は破棄されます。パケットに応答が必要な場合、非同期呼び出しは間違ったツールです。標準の同期呼び出しまたはレスポンスストリーミングを使用してください。
* AWSは障害時に再試行します:Lambdaは、失敗した非同期呼び出しを最大2回自動的に再試行します。関数(およびそれが開始するダウンストリームワークフロー)が冪等であることを確認するか、サイレントデータ損失なしに障害をキャプチャするためのデッドレターキューを設定してください。
* ストリーミングとの相互排他性:UseAsyncInvokeとUseResponseStreamingは、両方とも宛先でtrueにすることはできません。それらは反対の配信モデルを表します。
これらを組み合わせる
これら2つの機能は独立していますが、特定のパターンに自然に組み合わされます。それは、長時間実行されるバックエンドワークフローをトリガーするパブリックWireGuardエンドポイントです。デバイスプロビジョニングサービスを想像してみてください。デバイスは事前登録されたキーなしで製造されます。最初の起動時にキーペアを生成し、共有PSKを使用してオープンWireGuardリスナーに接続し、プロビジョニングリクエストパケットを送信します。Lambda関数がパケットを受信し、完全なプロビジョニングワークフロー(ID登録、証明書発行、DynamoDBレコード作成、SNS通知)を処理するStep Functionsステートマシンを開始し、返します。ステートマシンは数分または数時間実行されます。デバイスは即時の応答を受信しません。ワークフローが完了すると、プロビジョニング確認は別のチャネルを通じて送信されます。
これらのいずれも、デバイスが出荷される前にキーレジストリを管理する必要はありません。これらのいずれも、プロビジョニングの期間中、ゲートウェイが接続を開いたままにしておく必要はありません。イベント間のインフラストラクチャを実行することなく、パケットが入り、ワークフローが開始されます。同じパターンは、送信者が即時の応答を必要としない任意のインバウンドトリガーワークフローに適用されます。例えば、実行前に複数ステップの検証を開始するデバイスコマンドや、監査イベントなどです。