Consul
Run Lua scripts in Envoy proxy
The Lua Envoy extension enables the HTTP Lua filter in your Consul Envoy proxies, letting you run Lua scripts when requests and responses pass through Consul-generated Envoy resources.
Envoy filters support setting and getting dynamic metadata, allowing a filter to share state information with subsequent filters. To set dynamic metadata, configure the HTTP Lua filter. Users can call streamInfo:dynamicMetadata() from Lua scripts to get the request's dynamic metadata.
Configuration specifications
To use the Lua Envoy extension, configure the following arguments in the EnvoyExtensions block:
| Arguments | Type | Default | Description | 
|---|---|---|---|
| ListenerType | string | Required | Specifies if the extension is applied to the inboundoroutboundlistener. | 
| ProxyType | string | Required | Determines the proxy type the extension applies to. Supported values are connect-proxyandapi-gateway. | 
| Script | string | Required | The Lua script that is configured to run by the HTTP Lua filter. | 
Workflow
Complete the following steps to use the Lua Envoy extension:
- Configure EnvoyExtensions through service-defaultsorproxy-defaults.
- Apply the configuration entry.
Configure EnvoyExtensions
To use Envoy extensions, you must configure and apply a proxy-defaults or service-defaults configuration entry with the Envoy extension.
- When you configure Envoy extensions on proxy-defaults, they apply to every service.
- When you configure Envoy extensions on service-defaults, they apply to a specific service.
Consul applies Envoy extensions configured in proxy-defaults before it applies extensions in service-defaults. As a result, the Envoy extension configuration in service-defaults may  override configurations in proxy-defaults.
The following example configures the Lua Envoy extension on every service by using the proxy-defaults.
lua-envoy-extension-proxy-defaults.hcl
Kind = "proxy-defaults"
Name = "global"
Config {
  protocol = "http"
}
EnvoyExtensions {
  Name = "builtin/lua"
  Arguments = {
    ProxyType = "connect-proxy"
    Listener  = "inbound"
    Script    = <<-EOF
function envoy_on_request(request_handle)
  meta = request_handle:streamInfo():dynamicMetadata()
  m = meta:get("consul")
  request_handle:headers():add("x-consul-service", m["service"])
  request_handle:headers():add("x-consul-namespace", m["namespace"])
  request_handle:headers():add("x-consul-datacenter", m["datacenter"])
  request_handle:headers():add("x-consul-trust-domain", m["trust-domain"])
end
 EOF
  }
}
For a full list of parameters for EnvoyExtensions, refer to the service-defaults and proxy-defaults configuration entries reference documentation.
Warning
Applying `EnvoyExtensions` to `ProxyDefaults` may produce unintended consequences. We recommend enabling `EnvoyExtensions` with `ServiceDefaults` in most cases.Refer to Configuration specification section to find a full list of arguments for the Lua Envoy extension.
Apply the configuration entry
Apply the proxy-defaults or service-defaults configuration entry.
$ consul config write lua-envoy-extension-proxy-defaults.hcl
Examples
The following example configuration configures the Lua Envoy extension to insert the HTTP Lua filter for all Consul services named myservice and add the Consul service name to thex-consul-service header for all inbound requests. The ListenerType makes it so that the extension applies only on the inbound listener of the service's connect proxy.
lua-envoy-extension.hcl
Kind = "service-defaults"
Name = "myservice"
EnvoyExtensions = [
  {
    Name = "builtin/lua"
    Arguments = {
      ProxyType = "connect-proxy"
      Listener  = "inbound"
      Script    = <<EOF
  function envoy_on_request(request_handle)
    local service = request_handle:streamInfo():dynamicMetadata():get("consul")["service"]
    request_handle:headers():add("x-consul-service", service)
  end
  EOF
    }
  }
]
Alternatively, you can apply the same extension configuration to proxy-defaults configuration entries. This will apply to all services instead of the one you specified in the proxy default's name.
You can also specify multiple Lua filters through the Envoy extensions. They will not override each other.
lua-envoy-extension.hcl
Kind = "service-defaults"
Name = "myservice"
EnvoyExtensions = [
  {
    Name = "builtin/lua",
    Arguments = {
      ProxyType = "connect-proxy"
      Listener  = "inbound"
      Script    = <<-EOF
function envoy_on_request(request_handle)
  meta = request_handle:streamInfo():dynamicMetadata()
  m = meta:get("consul")
  request_handle:headers():add("x-consul-datacenter", m["datacenter1"])
end
      EOF
    }
  },
  {
    Name = "builtin/lua",
    Arguments = {
      ProxyType = "connect-proxy"
      Listener  = "inbound"
      Script    = <<-EOF
function envoy_on_request(request_handle)
  meta = request_handle:streamInfo():dynamicMetadata()
  m = meta:get("consul")
  request_handle:headers():add("x-consul-datacenter", m["datacenter2"])
end
      EOF
    }
  }
]
The following example configuration configures the Lua Envoy extension to insert the HTTP Lua filter for all Consul API Gateways named my-api-gateway to modify the response body when the status of the http request from upstream is 404.
lua-envoy-extension.hcl
  Kind = "service-defaults"
  Name = "my-api-gateway"
  EnvoyExtensions = [
  {
    Name = "builtin/lua",
    Arguments = {
      ProxyType = "api-gateway"
      Listener  = "outbound"
      Script    = <<EOF
        function envoy_on_response(response_handle)
          if response_handle:headers():get(":status") == "404" then
            local json = '{"message":"Modified by Lua script","status":"success"}'
            response_handle:body():setBytes(json)
            response_handle:headers():remove("content-length")
            response_handle:headers():replace("content-encoding", "identity")
            response_handle:headers():replace("content-type", "application/json")
          end
        end
      EOF
    }
  }
  ]