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:

  • ProxyType: string | connect-proxy - Determines the proxy type the extension applies to. The only supported value is connect-proxy.
  • ListenerType: string | required - Specifies if the extension is applied to the inbound or outbound listener.
  • Script: string | required - The Lua script that is configured to run by the HTTP Lua filter.

Workflow

There are two steps to configure the Lua Envoy extension:

  1. Configure EnvoyExtensions through service-defaults or proxy-defaults.
  2. 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.

Run Lua scripts in Envoy proxies - 图1

lua-envoy-extension-proxy-defaults.hcl

  1. Kind = "proxy-defaults"
  2. Name = "global"
  3. Protocol = "http"
  4. EnvoyExtensions {
  5. Name = "builtin/lua"
  6. Arguments = {
  7. ProxyType = "connect-proxy"
  8. Listener = "inbound"
  9. Script = <<-EOS
  10. function envoy_on_request(request_handle)
  11. meta = request_handle:streamInfo():dynamicMetadata()
  12. m = meta:get("consul")
  13. request_handle:headers():add("x-consul-service", m["service"])
  14. request_handle:headers():add("x-consul-namespace", m["namespace"])
  15. request_handle:headers():add("x-consul-datacenter", m["datacenter"])
  16. request_handle:headers():add("x-consul-trust-domain", m["trust-domain"])
  17. end
  18. EOS
  19. }
  20. }

Run Lua scripts in Envoy proxies - 图2

lua-envoy-extension-proxy-defaults.json

  1. {
  2. "kind": "proxy-defaults",
  3. "name": "global",
  4. "protocol": "http",
  5. "envoy_extensions": [{
  6. "name": "builtin/lua",
  7. "arguments": {
  8. "proxy_type": "connect-proxy",
  9. "listener": "inbound",
  10. "script": "function envoy_on_request(request_handle)\nmeta = request_handle:streamInfo():dynamicMetadata()\nm = \nmeta:get("consul")\nrequest_handle:headers():add("x-consul-service", m["service"])\nrequest_handle:headers():add("x-consul-namespace", m["namespace"])\nrequest_handle:headers():add("x-consul-datacenter", m["datacenter"])\nrequest_handle:headers():add("x-consul-trust-domain", m["trust-domain"])\nend"
  11. }
  12. }]
  13. }

Run Lua scripts in Envoy proxies - 图3

lua-envoy-extension-proxy-defaults.yaml

  1. apiVersion: consul.hashicorp.com/v1alpha1
  2. kind: ProxyDefaults
  3. metadata:
  4. name: global
  5. spec:
  6. protocol: http
  7. envoyExtensions:
  8. name = "builtin/lua"
  9. arguments:
  10. proxyType: "connect-proxy"
  11. listener: "inbound"
  12. script: |-
  13. function envoy_on_request(request_handle)
  14. meta = request_handle:streamInfo():dynamicMetadata()
  15. m = meta:get("consul")
  16. request_handle:headers():add("x-consul-service", m["service"])
  17. request_handle:headers():add("x-consul-namespace", m["namespace"])
  18. request_handle:headers():add("x-consul-datacenter", m["datacenter"])
  19. request_handle:headers():add("x-consul-trust-domain", m["trust-domain"])
  20. end

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.

  1. $ consul config write lua-envoy-extension-proxy-defaults.hcl
  1. $ consul config write lua-envoy-extension-proxy-defaults.json
  1. $ kubectl apply lua-envoy-extension-proxy-defaults.yaml

Examples

In the following example, the service-defaults configure the Lua Envoy extension to insert the HTTP Lua filter for service 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.

Run Lua scripts in Envoy proxies - 图4

lua-envoy-extension.hcl

  1. Kind = "service-defaults"
  2. Name = "myservice"
  3. EnvoyExtensions = [
  4. {
  5. Name = "builtin/lua"
  6. Arguments = {
  7. ProxyType = "connect-proxy"
  8. Listener = "inbound"
  9. Script = <<EOF
  10. function envoy_on_request(request_handle)
  11. local service = request_handle:streamInfo():dynamicMetadata():get("consul")["service"]
  12. request_handle:headers():add("x-consul-service", service)
  13. end
  14. EOF
  15. }
  16. }
  17. ]

Alternatively, you can apply the same extension configuration to proxy-defaults configuration entries.

You can also specify multiple Lua filters through the Envoy extensions. They will not override each other.

Run Lua scripts in Envoy proxies - 图5

lua-envoy-extension.hcl

  1. Kind = "service-defaults"
  2. Name = "myservice"
  3. EnvoyExtensions = [
  4. {
  5. Name = "builtin/lua",
  6. Arguments = {
  7. ProxyType = "connect-proxy"
  8. Listener = "inbound"
  9. Script = <<-EOF
  10. function envoy_on_request(request_handle)
  11. meta = request_handle:streamInfo():dynamicMetadata()
  12. m = meta:get("consul")
  13. request_handle:headers():add("x-consul-datacenter", m["datacenter1"])
  14. end
  15. EOF
  16. }
  17. },
  18. {
  19. Name = "builtin/lua",
  20. Arguments = {
  21. ProxyType = "connect-proxy"
  22. Listener = "inbound"
  23. Script = <<-EOF
  24. function envoy_on_request(request_handle)
  25. meta = request_handle:streamInfo():dynamicMetadata()
  26. m = meta:get("consul")
  27. request_handle:headers():add("x-consul-datacenter", m["datacenter2"])
  28. end
  29. EOF
  30. }
  31. }
  32. ]