consul/agent/envoyextensions/builtin/wasm/wasm.go

121 lines
4.2 KiB
Go
Raw Normal View History

2023-04-06 21:12:07 +00:00
// Copyright (c) HashiCorp, Inc.
[COMPLIANCE] License changes (#18443) * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2023-08-11 13:12:13 +00:00
// SPDX-License-Identifier: BUSL-1.1
2023-04-06 21:12:07 +00:00
package wasm
import (
"fmt"
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
envoy_http_wasm_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3"
envoy_wasm_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/wasm/v3"
2023-04-06 21:12:07 +00:00
"github.com/hashicorp/consul/api"
cmn "github.com/hashicorp/consul/envoyextensions/extensioncommon"
2023-04-06 21:12:07 +00:00
)
// wasm is a built-in Envoy extension that can patch filter chains to insert Wasm plugins.
type wasm struct {
cmn.BasicExtensionAdapter
2023-04-06 21:12:07 +00:00
name string
wasmConfig *wasmConfig
}
var supportedRuntimes = []string{"v8", "wamr", "wavm", "wasmtime"}
var _ cmn.BasicExtension = (*wasm)(nil)
2023-04-06 21:12:07 +00:00
func Constructor(ext api.EnvoyExtension) (cmn.EnvoyExtender, error) {
2023-04-06 21:12:07 +00:00
w, err := construct(ext)
if err != nil {
return nil, err
}
return &cmn.BasicEnvoyExtender{
2023-04-06 21:12:07 +00:00
Extension: &w,
}, nil
}
func construct(ext api.EnvoyExtension) (wasm, error) {
w := wasm{name: ext.Name}
if w.name != api.BuiltinWasmExtension {
return w, fmt.Errorf("expected extension name %q but got %q", api.BuiltinWasmExtension, w.name)
}
if err := w.fromArguments(ext.Arguments); err != nil {
return w, err
}
// Configure the failure behavior for the filter. If the plugin is required,
// then filter runtime errors result in a failed request (fail "closed").
// Otherwise, runtime errors result in the filter being skipped (fail "open").
w.wasmConfig.PluginConfig.failOpen = !ext.Required
return w, nil
}
func (w *wasm) fromArguments(args map[string]any) error {
var err error
w.wasmConfig, err = newWasmConfig(args)
if err != nil {
return fmt.Errorf("error decoding extension arguments: %w", err)
}
return w.wasmConfig.validate()
}
// CanApply indicates if the WASM extension can be applied to the given extension configuration.
func (w wasm) CanApply(cfg *cmn.RuntimeConfig) bool {
return cfg.Kind == w.wasmConfig.ProxyType &&
cfg.Protocol == w.wasmConfig.Protocol
}
2023-04-06 21:12:07 +00:00
func (w wasm) matchesConfigDirection(isInboundListener bool) bool {
return (isInboundListener && w.wasmConfig.ListenerType == "inbound") ||
(!isInboundListener && w.wasmConfig.ListenerType == "outbound")
2023-04-06 21:12:07 +00:00
}
// PatchFilters adds a Wasm HTTP or TCP filter to the filter chain.
func (w wasm) PatchFilters(cfg *cmn.RuntimeConfig, filters []*envoy_listener_v3.Filter, isInboundListener bool) ([]*envoy_listener_v3.Filter, error) {
if !w.matchesConfigDirection(isInboundListener) {
return filters, nil
}
// Check that the Wasm plugin protocol matches the service protocol.
// It is a runtime error if the extension is configured to apply a Wasm plugin
// that doesn't match the service's protocol.
// This shouldn't happen because the caller should check CanApply first, but just in case.
if cfg.Protocol != w.wasmConfig.Protocol {
return filters, fmt.Errorf("failed to apply Wasm filter: service protocol for %q is %q but protocol for the Wasm extension is %q. Please ensure the protocols match",
cfg.ServiceName.Name, cfg.Protocol, w.wasmConfig.Protocol)
2023-04-06 21:12:07 +00:00
}
// Generate the Wasm plugin configuration. It is the same config for HTTP and network filters.
2023-04-06 21:12:07 +00:00
wasmPluginConfig, err := w.wasmConfig.PluginConfig.envoyPluginConfig(cfg)
if err != nil {
return filters, fmt.Errorf("failed to encode Envoy Wasm plugin configuration: %w", err)
2023-04-06 21:12:07 +00:00
}
// Insert the filter immediately before the terminal filter.
insertOptions := cmn.InsertOptions{Location: cmn.InsertBeforeFirstMatch}
2023-04-06 21:12:07 +00:00
switch cfg.Protocol {
case "grpc", "http2", "http":
insertOptions.FilterName = "envoy.filters.http.router"
filter, err := cmn.MakeEnvoyHTTPFilter("envoy.filters.http.wasm", &envoy_http_wasm_v3.Wasm{Config: wasmPluginConfig})
if err != nil {
return filters, fmt.Errorf("failed to make Wasm HTTP filter: %w", err)
2023-04-06 21:12:07 +00:00
}
return cmn.InsertHTTPFilter(filters, filter, insertOptions)
case "tcp":
fallthrough
default:
insertOptions.FilterName = "envoy.filters.network.tcp_proxy"
filter, err := cmn.MakeFilter("envoy.filters.network.wasm", &envoy_wasm_v3.Wasm{Config: wasmPluginConfig})
if err != nil {
return filters, fmt.Errorf("failed to make Wasm network filter: %w", err)
}
return cmn.InsertNetworkFilter(filters, filter, insertOptions)
2023-04-06 21:12:07 +00:00
}
}