mirror of https://github.com/status-im/consul.git
142 lines
4.4 KiB
Go
142 lines
4.4 KiB
Go
|
package lua
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
|
||
|
envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
||
|
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||
|
envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
||
|
envoy_lua_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/lua/v3"
|
||
|
envoy_http_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
||
|
envoy_resource_v3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3"
|
||
|
"github.com/hashicorp/go-multierror"
|
||
|
"github.com/mitchellh/mapstructure"
|
||
|
|
||
|
"github.com/hashicorp/consul/agent/xds/builtinextensiontemplate"
|
||
|
"github.com/hashicorp/consul/agent/xds/xdscommon"
|
||
|
"github.com/hashicorp/consul/api"
|
||
|
)
|
||
|
|
||
|
type lua struct {
|
||
|
ProxyType string
|
||
|
Listener string
|
||
|
Script string
|
||
|
}
|
||
|
|
||
|
var _ builtinextensiontemplate.Plugin = (*lua)(nil)
|
||
|
|
||
|
// MakeLuaExtension is a builtinextensiontemplate.PluginConstructor for a builtinextensiontemplate.EnvoyExtension.
|
||
|
func MakeLuaExtension(ext xdscommon.ExtensionConfiguration) (builtinextensiontemplate.Plugin, error) {
|
||
|
var resultErr error
|
||
|
var plugin lua
|
||
|
|
||
|
if name := ext.EnvoyExtension.Name; name != api.BuiltinLuaExtension {
|
||
|
return nil, fmt.Errorf("expected extension name 'lua' but got %q", name)
|
||
|
}
|
||
|
|
||
|
if err := mapstructure.Decode(ext.EnvoyExtension.Arguments, &plugin); err != nil {
|
||
|
return nil, fmt.Errorf("error decoding extension arguments: %v", err)
|
||
|
}
|
||
|
|
||
|
if plugin.Script == "" {
|
||
|
resultErr = multierror.Append(resultErr, fmt.Errorf("Script is required"))
|
||
|
}
|
||
|
|
||
|
if err := validateProxyType(plugin.ProxyType); err != nil {
|
||
|
resultErr = multierror.Append(resultErr, err)
|
||
|
}
|
||
|
|
||
|
if err := validateListener(plugin.Listener); err != nil {
|
||
|
resultErr = multierror.Append(resultErr, err)
|
||
|
}
|
||
|
|
||
|
return plugin, resultErr
|
||
|
}
|
||
|
|
||
|
func validateProxyType(t string) error {
|
||
|
if t != "connect-proxy" {
|
||
|
return fmt.Errorf("unexpected ProxyType %q", t)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func validateListener(t string) error {
|
||
|
if t != "inbound" && t != "outbound" {
|
||
|
return fmt.Errorf("unexpected Listener %q", t)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// CanApply determines if the extension can apply to the given extension configuration.
|
||
|
func (p lua) CanApply(config xdscommon.ExtensionConfiguration) bool {
|
||
|
return string(config.Kind) == p.ProxyType && p.matchesListenerDirection(config)
|
||
|
}
|
||
|
|
||
|
func (p lua) matchesListenerDirection(config xdscommon.ExtensionConfiguration) bool {
|
||
|
return (config.IsUpstream() && p.Listener == "outbound") || (!config.IsUpstream() && p.Listener == "inbound")
|
||
|
}
|
||
|
|
||
|
// PatchRoute does nothing.
|
||
|
func (p lua) PatchRoute(route *envoy_route_v3.RouteConfiguration) (*envoy_route_v3.RouteConfiguration, bool, error) {
|
||
|
return route, false, nil
|
||
|
}
|
||
|
|
||
|
// PatchCluster does nothing.
|
||
|
func (p lua) PatchCluster(c *envoy_cluster_v3.Cluster) (*envoy_cluster_v3.Cluster, bool, error) {
|
||
|
return c, false, nil
|
||
|
}
|
||
|
|
||
|
// PatchFilter inserts a lua filter directly prior to envoy.filters.http.router.
|
||
|
func (p lua) PatchFilter(filter *envoy_listener_v3.Filter) (*envoy_listener_v3.Filter, bool, error) {
|
||
|
if filter.Name != "envoy.filters.network.http_connection_manager" {
|
||
|
return filter, false, nil
|
||
|
}
|
||
|
if typedConfig := filter.GetTypedConfig(); typedConfig == nil {
|
||
|
return filter, false, errors.New("error getting typed config for http filter")
|
||
|
}
|
||
|
|
||
|
config := envoy_resource_v3.GetHTTPConnectionManager(filter)
|
||
|
if config == nil {
|
||
|
return filter, false, errors.New("error unmarshalling filter")
|
||
|
}
|
||
|
luaHttpFilter, err := makeEnvoyHTTPFilter(
|
||
|
"envoy.filters.http.lua",
|
||
|
&envoy_lua_v3.Lua{
|
||
|
InlineCode: p.Script,
|
||
|
},
|
||
|
)
|
||
|
if err != nil {
|
||
|
return filter, false, err
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
changedFilters = make([]*envoy_http_v3.HttpFilter, 0, len(config.HttpFilters)+1)
|
||
|
changed bool
|
||
|
)
|
||
|
|
||
|
// We need to be careful about overwriting http filters completely because
|
||
|
// http filters validates intentions with the RBAC filter. This inserts the
|
||
|
// lua filter before envoy.filters.http.router while keeping everything
|
||
|
// else intact.
|
||
|
for _, httpFilter := range config.HttpFilters {
|
||
|
if httpFilter.Name == "envoy.filters.http.router" {
|
||
|
changedFilters = append(changedFilters, luaHttpFilter)
|
||
|
changed = true
|
||
|
}
|
||
|
changedFilters = append(changedFilters, httpFilter)
|
||
|
}
|
||
|
if changed {
|
||
|
config.HttpFilters = changedFilters
|
||
|
}
|
||
|
|
||
|
newFilter, err := makeFilter("envoy.filters.network.http_connection_manager", config)
|
||
|
if err != nil {
|
||
|
return filter, false, errors.New("error making new filter")
|
||
|
}
|
||
|
|
||
|
return newFilter, true, nil
|
||
|
}
|