envoyにBasic認証実装

envoyは非常に高機能なプロキシですが、Basic認証のフィルタ機能はありません。 Set basic authentication to the endpointで議論されているように、同等の機能をLuaスクリプトで実装することは可能です。

issueの リンクに掲載されているコードは、 RFC7617のロジックを実装したものです。Basic認証そのものの説明になってしまいますが、概略は以下のような処理です。

  • authorizationヘッダーの内容のマッチ検証
    • “Basic b2s6dGVzdDE=“の形式であること。BasicはRFC7617の指定する定型句
    • “b2s6dGVzdDE=“の部分は、“id:pass"形式の文字列をBase64エンコードしたもの
  • 認証NGの場合は、RFC7617の指定するヘッダーとともに、HTTP401をレスポンス

issueの実装例は古いため、 APIv3に合わせて実装する必要があります。

Luaの機能は、公式Dockerにインクルードされており、単にconfigにコードを記載するだけで動作します。リファレンスに利用例の解説があります。関数envoy_on_request()はリクエストをフックするイベントハンドラです。

動作確認は、ブラウザでアクセスしながら確認すると良いでしょう。また、無効な設定を書いた場合、envoyの起動メッセージにエラー出力されるため、合わせて確認が必要です。

Basic認証じたいが柔軟性の低い認証方式であるため、直接の機能サポートは手薄ですが、適切なLuaスクリプトを書けば目的の挙動にはなります。

Per Route Configuration

Luaのフィルタはlistner.filter_chains[].filters[].http_filters[]に書けます。

Luaの呼び出し方はいくつかの方式がありますが、 Per Route Configurationという構成が主流となりました。
source_codesに名前付きの関数(下の例ではbasicauth.lua)として登録しておきます。

        http_filters:
        - name: envoy.filters.http.lua
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
            source_codes:
              basicauth.lua:
                inline_string: |
                  function envoy_on_request(request_handle)
                    if string.match(request_handle:headers():get(":path"), "/protected_path")
                    then
                      if request_handle:headers():get("authorization") == "Basic b2s6dGVzdDE="
                      then
                        return
                      end
                      request_handle:respond(
                        {[":status"] = "401", ["WWW-Authenticate"] = "Basic realm=\"Unknown\""}, "Unauthorized"
                      )
                    end
                  end

            # inline_code is deprecated in v1.23.0
            inline_code: |
              function envoy_on_request(request_handle)
                -- do nothing
                return
              end

        - name: envoy.filters.http.router

v1.22以前ではinline_codeは必須項目であるため、上の例のように何もしないダミーコードを登録しておく必要があります。
また、コード末尾のenvoy.filters.http.routerは一般的なルーティングのフィルタです。定義階層の参考であり、Luaを利用するためのコードではありません。

呼び出しの制御は、以下のようにrouteのtyped_per_filter_configで定義できます。なお、typed_per_filter_configはVirtualHostのレイヤにも定義可能です。

  "@type": type.googleapis.com/envoy.config.route.v3.RouteConfiguration
  virtual_hosts:
  - name: example
    domains:
    - "example.com"
    routes:
    - match:
        prefix: "/protected_path"
      route:
        cluster: upstream_host
      typed_per_filter_config:
        envoy.filters.http.lua:
          "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute
          name: basicauth.lua
    - match:
        prefix: "/"
      route:
        cluster: upstream_host
      typed_per_filter_config:
        envoy.filters.http.lua:
          "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute
          disabled: true

LuaPerRouteの設定には、disabledまたはnameを指定できます。

中馬崇尋
Chuma Takahiro