İçeriğe Atla
Mustafa Erbay
Tutorials · 9 min read · görüntülenme Türkçe oku
100%

Internal API Authorization Chain with Envoy ext_authz

A secure authorization pipeline you can build with the Envoy ext_authz filter to separate identity, policy, and decision logging on internal service traffic.

Internal API Authorization Chain with Envoy ext_authz — cover image

The most common security failure I see in internal APIs is when authentication and authorization both end up baked into the same application code, and over time each service starts behaving differently. One service accepts a JWT based on signature alone, another inspects claims, and a third interprets a service identity instead of a user. Envoy’s ext_authz filter offers a practical middle layer here: the request lands at the proxy first, the decision is delegated to a separate authorization service, and the application is free to focus only on business logic.

Technical schema showing the internal API authorization chain with Envoy ext_authz
Building shared security checks at the proxy layer reduces the scattered authorization decisions inside application code.

What problem does this solve?

Services running on the internal network often grow under the assumption that “we’re already in a trusted zone.” Then, as integrations multiply, these issues surface:

  • The same identity is interpreted with different rights across services.
  • A policy change requires deploying every service one at a time.
  • When a request is denied, the reason behind the decision isn’t visible centrally.
  • The operations team can’t tell whether a 403 is caused by network, token, or policy.

So the ext_authz model offers more than just security; it also brings operational readability.

Target architecture

The basic flow looks like this:

  1. The client request arrives at Envoy.
  2. Envoy forwards request metadata to the ext_authz service.
  3. The authorization service evaluates identity and policy.
  4. Envoy lets the request through or rejects it.
  5. The decision record is sent to a centralized log or metrics pipeline.

In this model, the application service can safely assume the request has already been pre-filtered for authorization.

Minimal Envoy configuration

The example below shows how ext_authz is wired into the HTTP filter chain:

static_resources:
  listeners:
    - name: internal-api
      address:
        socket_address:
          address: 0.0.0.0
          port_value: 8443
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                stat_prefix: internal_api
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: api
                      domains: ["*"]
                      routes:
                        - match: { prefix: "/" }
                          route: { cluster: app }
                http_filters:
                  - name: envoy.filters.http.ext_authz
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
                      transport_api_version: V3
                      failure_mode_allow: false
                      grpc_service:
                        envoy_grpc:
                          cluster_name: authz
                  - name: envoy.filters.http.router
  clusters:
    - name: authz
      connect_timeout: 1s
      type: STRICT_DNS
      load_assignment:
        cluster_name: authz
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: authz.internal
                      port_value: 9000
    - name: app
      connect_timeout: 1s
      type: STRICT_DNS
      load_assignment:
        cluster_name: app
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: app.internal
                      port_value: 8080

The critical choice here is failure_mode_allow: false. If your authorization service can’t respond, denying the request in a controlled way is safer than letting it through. But you also need to plan the operational impact of that decision in advance.

What should the authorization service look at?

I prefer to keep the ext_authz service as small as possible, with crisp responsibilities. These are the fields I treat as fundamental:

  • The service or user identity behind the request
  • Target path, method, and tenant context
  • The required role or policy name
  • A short reason code that explains why the decision was made

Letting this service drift into business logic is a mistake. Order limits, campaign rules, or customer segmentation should remain the application’s responsibility; the ext_authz layer should be limited to the access decision.

How should the test flow be designed?

Before you take this pipeline live, automate three scenarios:

  • A request that is allowed with a valid identity
  • A request that should be denied even with a valid identity
  • System behavior when the authorization service is slow or unreachable

The third scenario is especially critical. Many teams test normal traffic, but they don’t observe how Envoy’s queues and client timeouts behave when the authorization service slows down.

Operational observability layer

Once the setup is complete, watch these signals:

  • Total request count and the proportion being denied
  • Distribution of denials by reason code
  • ext_authz response time
  • Error rate between the proxy and the authorization service

Without these metrics, the security layer looks centralized on the surface but stays opaque in practice.

Conclusion

Building an internal API authorization chain with Envoy ext_authz doesn’t force you to rip access decisions out of service code entirely; what it does is move them onto a shared, observable, manageable layer. When identity, policy, and decision records are separated, the security team gets more consistent control, the platform team diagnoses problems faster, and application teams can stay focused on business logic.

Paylaş:

Bu yazı faydalı oldu mu?

Yükleniyor...

Bu yazı nasıldı?

ME

Mustafa Erbay

Sistem Mimarisi · Network Uzmanı · Altyapı, Güvenlik ve Yazılım

2006'dan bu yana sistem mimarisi, network, sunucu altyapıları, büyük yapıların kurulumu, yazılım ve sistem güvenliği ekseninde çalışıyorum. Bu blogda sahada karşılığı olan teknik deneyimlerimi paylaşıyorum.

Kişisel Notlar

Bu notlar sadece sizde saklanır. Tarayıcınızda yerel olarak tutulur.

Hazır 0 karakter

Comments

Server-side AI Moderation

Comments are AI-moderated server-side and stored permanently.

?
0/2000

Server-side AI moderation

✉️ Free · No spam · Unsubscribe anytime

Curated digest, hand-picked by me — not the AI

Once a week: the most important post of the week, behind-the-scenes notes, and a "what I actually used this week" section. Less noise, more signal.

  • 📌
    Best of the week Single most-worth-reading post
  • 🔧
    Toolbox notes Real tools I used this week
  • 🧠
    Behind-the-scenes Notes that don't make it to blog

We don't spam. Unsubscribe anytime. · Tracked only by Umami (self-hosted, no Google).

Your Reading Stats

0

Posts Read

0m

Reading Time

0

Day Streak

-

Favorite Category

Related Posts