1️⃣

(originキッチン in go)access-control-allow-originのたった1行でALBがunhealthyになった話

2023/07/16に公開

概要


golangを用いたlambda関数にて、エラーレスポンスを返すためにaccess-control-allow-originを動的に設定するという1行を入れただけで、処理はエラーが出ていないのに、HealthCheckだけがUnHealthyになってしまったお話

経緯


postをjsから飛ばしてapi gateway → lambda...といった処理の際に、ALBの稼働状態を監視するHealthCheckを設定しているのですが、golangで構成しているlambda関数処理にて、HealthCheckはgetなので、以下のように、処理の最初にリクエストの種類がgetならばstatus okと返却する処理を入れることでcheckを実装しておりました。

var multiValueHeaders = map[string][]string{
    "Content-Type":                     {"application/json"},
    "Access-Control-Allow-Methods":     {"OPTIONS,POST,GET"},
    "Access-Control-Allow-Headers":     {"Origin,Authorization,Accept,X-Requested-With"},
    "Access-Control-Allow-Credentials": {"true"},
}


if gatewayReq.HTTPMethod == http.MethodGet {
    return events.APIGatwayProxyResponse {
        StatusCode: http.StatusOK,
        MultiValueHeaders: multiValueHeaders,
        Body: "",
    }
}

*複数のヘッダーを返す必要があったのでMultiValueHeadersを設定、以下のような通常時のTargetGroupの設定欄のTarget configurationをOffからOnにしております。

https://dev.classmethod.jp/articles/alb-lambda-multi-values-header-setting/

そして開発は進み、
develop環境→qa環境も作成して、production環境に各処理を反映させた!というところで、
HealthCheckがunhealthyになってないか...?ということに気づきます。
あるあるなのがMulti value headersをOffのままにしているところなのですが、Onですし、lambdaのハンドラ名はもちろん、先祖返りなどもなくなぜ??となりました。
なぜなら直近でhealth check関連の処理を弄った記憶はなく、HealthCheckだけunhealthyになり、通常の処理は問題なく実行されるからです。
リリースも近づいているこの時点でこのようなエラーが生じ、なぜかと小一時間悩みました....

解決策


原因だけ簡潔に述べると、2週間以上前に、パースエラーが発生した際に、ブラウザ側にログ送信、400レスポンスを返却するためにAccess-Control-Allow-Originを設定する処理を入れるようにしておりました。
その際にAccess-Control-Allow-Originは一つのドメインしか設定できないので、動的に生成するようにしておりました。

// Access-Control-Allow-Originは1つのドメインしか設定できないので動的に生成する
    origin, ok := gatewayReq.MultiValueHeaders["origin"]

これが駄目で、以下のように空ではないかどうかの条件分岐を入れてやることでhealthyに戻りました!

// Access-Control-Allow-Originは1つのドメインしか設定できないので動的に生成する
    origin, ok := gatewayReq.MultiValueHeaders["origin"]
    if ok {
        multiValueHeaders["Access-Control-Allow-Origin"] = origin
    }

原因


ではたった1行入れるだけでHealthCheckがunhealthyになった原因は何なのか...
それはHealthCheckのリクエストの仕組みによるものでした。

HealthCheckの際のリクエストは
特定のドメインからリクエストを行うものではなく、直接ALBから送信されます。
なのでgatewayReq.MultiValueHeaders["origin"]が空(nil)になってしまうのです。
このようになってしまうと、
特定のドメイン or *(任意のオリジン) を指定する必要のあるAccess-Control-Allow-Originヘッダが空のまま設定され、適切なヘッダ設定ではないリクエストが飛び、HealthCheckのみunhealthyになってしまっていたのです。
なので解決策のように条件分岐で空文字の時は設定しないようにしてやることでhealthyになったというような流れでした。
https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/target-group-health-checks.html

調査方法


仮定の作成
まずは大まかに原因を分割します。

  1. コードが悪いのか
  2. 設定してもらったAWSリソースが他環境と違うのか

どちらかを判別するために、healthyになっていると思っているdevelop環境を確認しました。
そしたらproduction環境だけではなくdevelop環境もunhealthyに、awsリソース作成者の方からdevelop環境変更の報告は来ていないので1が濃厚としてさらに原因を絞ります。

時期の絞り込み
次にコードがおそらく悪いだろうと仮定しましたので、いつからunhealthyになったかを調査します。
aws管理画面のTargetGroupのモニタリングの画面では期間を調節してHealthCheckの結果を確認できます。
すると2週間以上の前の日付から急にunhealthyになっていたことが判明しました。

該当するバージョンのコードを探す
unhealthyになった時期がわかったので、次はその時期のコードを見に行きます。
githubでもvscodeのタイムラインでもいいのですが、その時期のバージョンを見るとちょうどほぼ同時期に上記のAccess-Control-Allow-Originに関する処理が入っていたので、これではないかと思い、この処理を一時的に削除してみたところ、healthyになったのでエラー該当箇所に絞り込みが完了できました。

さいごに


たった1行、されど1行を存分に味わったような体験でした。原因がわかり、対応できたのは良いですが、そもそも開発中とはいえ、unhealthy状態であるのを2週間以上気付けないというのはいかがなものなのかと思いました。どう改善しようか...

参考文献
https://dev.classmethod.jp/articles/alb-lambda-multi-values-header-setting/

https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/target-group-health-checks.html

Discussion