🛡️

Fastly Compute セキュリティ系機能 (2) NGWAF の利用

2024/12/15に公開

この記事は Fastly Compute 一人アドベントカレンダー 2024 15 日目の記事です。

今年 Compute に追加された主な機能のうち、セキュリティ機能の概要について紹介するシリーズの第二回目です。前回の Edge Rate Limit に引き続き、本稿では Fastly がセキュリティソリューションとして提供する WAF サービスである NGWAF を Compute から利用する方法について紹介します。

Compute から見た NGWAF のインターフェース

NGWAF で呼び出せる機能については Rust SDK のリファレンスで確認することが可能です。https://docs.rs/fastly/latest/fastly/security/index.html

見てみると分かるのですがインターフェースはかなりシンプルで、inspect() 関数を呼び出すことのみが現状はできそうです。そして名前から推察するに、何か知らのペイロードを inspect(検査)してくれることが期待できそうです。実際のコード例を見てみましょう。

#[fastly::main]
fn main(client_req: Request) -> Result<Response, fastly::Error> {
  let (req_handle, req_body) = client_req.into_handles();
  let req_body = req_body.unwrap_or_else(|| BodyHandle::new());
  let config = InspectConfig::new(&req_handle, &req_body)
    .corp("test_corp")
    .workspace("test_site");

  match inspect(config) {
    Ok(resp) => match resp.verdict() {
      InspectVerdict::Block => Ok(Response::from_status(StatusCode::NOT_ACCEPTABLE)),
      InspectVerdict::Allow => {
        Ok(Request::from_handles(req_handle, Some(req_body)).send("content_backend")?)
      },
      InspectVerdict::Unauthorized => {
        panic!("The service is not authorized to inspect the request")
      },
      _ => Ok(Response::from_status(StatusCode::INTERNAL_SERVER_ERROR)
        .with_body("Unable to inspect request")),
    },
    Err(err) => {
      let msg = format!("Invalid request: {err:?}");
      Ok(Response::from_status(StatusCode::BAD_REQUEST).with_body(msg))
    }
  }
}

inspect() 関数呼び出しの際に config 変数としてリクエストハンドルやボディの情報を渡して呼び出しており、この引数の内容が WAF によって攻撃判定などに使われることになります。NGWAF からは、inspect() 呼び出しの結果の戻り値として InspectVerdict::BlockInspectVerdict::Allow などの値が得られます。この処理フローを活用して、例えば

  • 特定の path に届いたリクエストについてのみ検査を行う(例: /admin 以下へのアクセスのみ検査、等)
  • 未認証のユーザからのアクセスの場合のみ検査を行う
  • 特定の地域からのアクセスの場合のみ検査を行う(例:日本国外からのアクセスについてのみ、等)

など、Compute サービス側で NGWAF の検査の結果を活用しながら任意の処理やレスポンス生成を柔軟に行うことが可能です。

デプロイ方法

制御できるおおよその内容がわかったところで、次にデプロイの手順について開発者ドキュメントに記載がありますので、こちらの内容についても紹介しておきます。

https://www.fastly.com/documentation/solutions/tutorials/next-gen-waf-compute/

といってもあまり複雑なことは書かれておらず、コードも本稿で既に解説したものですので時間がない方はとりあえず以下の手順で進めれば OK です;

(1) fastly compute init 等でプロジェクトを初期化
(2) 上記開発者ドキュメントで紹介されている Rust のコードを main.rs として保存
(3) fastly compute publish でサービスをデプロイ(その際の対話シェルの中で Host 名が content_backend である Static backend を設定するのを忘れずに)

なお、(2) の際にですが、開発者ドキュメントで紹介されているコードでは ConfigStore を使って NGWAF のいくつかの設定を保持していますが、試してみる用途であれば ConfigStore を使わずに設定を main.rs にハードコードしても問題ないので簡単に済ませたい方は本稿で紹介したイメージでハードコードしてしまって構いません。その方が手順が短く済むので楽に進められるかと思います。

上記手順によって Compute サービスのデプロイが終わり、xxx-yyy-zzz.edgecompute.app のような URL が払い出されたら、ブラウザでアクセスすると以下のようなエラーが出るはずです。

Invalid request: RequestError(FastlyStatus::ERROR)

まだこの時点では NGWAF のデプロイを行なっておらず Compute サービスのデプロイを実施したのみですので、このエラーが出るのが期待値となります。ここまで完了すると、あとは以下の REST API のエンドポイントを一回叩けばデプロイ完了となります。(該当する開発者ドキュメントの手順はこちらです)

$ curl -X PUT "https://dashboard.signalsciences.net/api/v0/corps/${corpName}/sites/${siteName}/edgeDeployment" \
H "x-api-user:${SIGSCI_EMAIL}" \
H "x-api-token:${SIGSCI_TOKEN}" \
H "Fastly-Key: ${FASTLY_KEY}" \
H "Content-Type: application/json" \
d '{"authorizedServices": [ "${fastlySID}" ] }'

上記実行すると、{} という空のレスポンスボディとともにステータスコード 200 が返却されるはずです。この API を叩いた後、数分すると先ほど出ていた Invalid request: RequestError(FastlyStatus::ERROR) が消えて、無事 NGWAF による inspect() が正常に機能を開始します。

その他の参考リソース

NGWAF でできることや機能の概要については以下のリンクの情報などが参考になるかと思います。

https://qiita.com/AtsushiFukuda/items/16b143a849d442d182da
https://engineer.retty.me/entry/2021/12/19/120000
https://dev.classmethod.jp/articles/deploy-ngwaf/

Compute サービスからの呼び出しについてはつい先日リリースされたばかりの機能でそれ自体を解説している情報は多くないのですが、NGWAF の特徴や構築後の運用方法のイメージなどの理解に役立つと思います。上記記事を既にご存知の方も多いかもしれませんが、念の為リンクを貼っておきます。

まとめ

本稿では Fastly がセキュリティソリューションとして提供する WAF サービスである NGWAF を Compute から利用する方法について紹介しました。WAF は典型的には全体のアーキテクチャの中の一レイヤとして存在しておりコード上から制御すること自体が少ないかと思うので、今回のようにコード上から機能呼び出しする感覚は個人的には興味深く感じていますし、コード上で追加で独自の防御レイヤを設計・実装したりすることなども容易にできる点がエッジコンピューティングを使った防御層を構築する上で便利な点の一つかと思います。

次回は Compute と DevOps(CI/CD) という切り口のシリーズ第一回目として、Rust/Go/JS 各 SDK でのテストの書き方について概要や使い方を紹介します。

Discussion