Skip to content

Commit

Permalink
mesh: add validation hook to proxy configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
ishustava authored and rboyer committed Oct 13, 2023
1 parent 3d1a606 commit b08d9d4
Show file tree
Hide file tree
Showing 7 changed files with 402 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ func TestSortProxyConfigurations(t *testing.T) {
var decProxyCfgs []*types.DecodedProxyConfiguration
for i, ws := range c.selectors {
proxyCfg := &pbmesh.ProxyConfiguration{
Workloads: ws,
Workloads: ws,
DynamicConfig: &pbmesh.DynamicConfig{},
}
resName := fmt.Sprintf("cfg-%d", i)
proxyCfgRes := resourcetest.Resource(pbmesh.ProxyConfigurationType, resName).
Expand Down
11 changes: 11 additions & 0 deletions internal/mesh/internal/types/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package types

import (
"errors"
)

var (
errInvalidPort = errors.New("port number is outside the range 1 to 65535")
errInvalidExposePathProtocol = errors.New("invalid protocol: only HTTP and HTTP2 protocols are allowed")
errMissingProxyConfigData = errors.New("at least one of \"bootstrap_config\" or \"dynamic_config\" fields must be set")
)
189 changes: 176 additions & 13 deletions internal/mesh/internal/types/proxy_configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ package types
import (
"github.com/hashicorp/go-multierror"

"github.com/hashicorp/consul/internal/catalog"
"math"

"github.com/hashicorp/go-multierror"

"github.com/hashicorp/consul/internal/catalog"
"github.com/hashicorp/consul/internal/resource"
pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1"
Expand All @@ -15,9 +20,9 @@ import (

func RegisterProxyConfiguration(r resource.Registry) {
r.Register(resource.Registration{
Type: pbmesh.ProxyConfigurationType,
Proto: &pbmesh.ProxyConfiguration{},
Scope: resource.ScopeNamespace,
Type: pbmesh.ProxyConfigurationType,
Proto: &pbmesh.ProxyConfiguration{},
Scope: resource.ScopeNamespace,
Mutate: MutateProxyConfiguration,
Validate: ValidateProxyConfiguration,
})
Expand Down Expand Up @@ -53,23 +58,181 @@ func MutateProxyConfiguration(res *pbresource.Resource) error {
}

func ValidateProxyConfiguration(res *pbresource.Resource) error {
var cfg pbmesh.ProxyConfiguration

if err := res.Data.UnmarshalTo(&cfg); err != nil {
return resource.NewErrDataParse(&cfg, err)
decodedProxyCfg, decodeErr := resource.Decode[*pbmesh.ProxyConfiguration](res)
if decodeErr != nil {
return resource.NewErrDataParse(decodedProxyCfg.GetData(), decodeErr)
}
proxyCfg := decodedProxyCfg.GetData()

var merr error
var err error

// Validate the workload selector
if selErr := catalog.ValidateSelector(cfg.Workloads, false); selErr != nil {
merr = multierror.Append(merr, resource.ErrInvalidField{
if selErr := catalog.ValidateSelector(proxyCfg.Workloads, false); selErr != nil {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "workloads",
Wrapped: selErr,
})
}

// TODO(rb): add more validation for proxy configuration
if proxyCfg.GetDynamicConfig() == nil && proxyCfg.GetBootstrapConfig() == nil {
err = multierror.Append(err, resource.ErrInvalidFields{
Names: []string{"dynamic_config", "bootstrap_config"},
Wrapped: errMissingProxyConfigData,
})
}

// nolint:staticcheck
if proxyCfg.GetOpaqueConfig() != nil {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "opaque_config",
Wrapped: resource.ErrUnsupported,
})
}

if dynamicCfgErr := validateDynamicProxyConfiguration(proxyCfg.GetDynamicConfig()); dynamicCfgErr != nil {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "dynamic_config",
Wrapped: dynamicCfgErr,
})
}

return err
}

func validateDynamicProxyConfiguration(cfg *pbmesh.DynamicConfig) error {
if cfg == nil {
return nil
}

var err error

// Error if any of the currently unsupported fields is set.
if cfg.GetMutualTlsMode() != pbmesh.MutualTLSMode_MUTUAL_TLS_MODE_DEFAULT {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "mutual_tls_mode",
Wrapped: resource.ErrUnsupported,
})
}

if cfg.GetMeshGatewayMode() != pbmesh.MeshGatewayMode_MESH_GATEWAY_MODE_UNSPECIFIED {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "mesh_gateway_mode",
Wrapped: resource.ErrUnsupported,
})
}

if cfg.GetAccessLogs() != nil {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "access_logs",
Wrapped: resource.ErrUnsupported,
})
}

if cfg.GetEnvoyExtensions() != nil {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "envoy_extensions",
Wrapped: resource.ErrUnsupported,
})
}

if cfg.GetPublicListenerJson() != "" {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "public_listener_json",
Wrapped: resource.ErrUnsupported,
})
}

if cfg.GetListenerTracingJson() != "" {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "listener_tracing_json",
Wrapped: resource.ErrUnsupported,
})
}

if cfg.GetLocalClusterJson() != "" {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "local_cluster_json",
Wrapped: resource.ErrUnsupported,
})
}

// nolint:staticcheck
if cfg.GetLocalWorkloadAddress() != "" {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "local_workload_address",
Wrapped: resource.ErrUnsupported,
})
}

// nolint:staticcheck
if cfg.GetLocalWorkloadPort() != 0 {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "local_workload_port",
Wrapped: resource.ErrUnsupported,
})
}

// nolint:staticcheck
if cfg.GetLocalWorkloadSocketPath() != "" {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "local_workload_socket_path",
Wrapped: resource.ErrUnsupported,
})
}

if tproxyCfg := cfg.GetTransparentProxy(); tproxyCfg != nil {
if tproxyCfg.DialedDirectly {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "transparent_proxy",
Wrapped: resource.ErrInvalidField{
Name: "dialed_directly",
Wrapped: resource.ErrUnsupported,
},
})
}

if outboundListenerPortErr := validatePort(tproxyCfg.OutboundListenerPort, "outbound_listener_port"); outboundListenerPortErr != nil {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "transparent_proxy",
Wrapped: outboundListenerPortErr,
})
}
}

if exposeCfg := cfg.GetExposeConfig(); exposeCfg != nil {
for i, path := range exposeCfg.GetExposePaths() {
if listenerPortErr := validatePort(path.ListenerPort, "listener_port"); listenerPortErr != nil {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "expose_config",
Wrapped: resource.ErrInvalidListElement{
Name: "expose_paths",
Index: i,
Wrapped: listenerPortErr,
},
})
}

return merr
if localPathPortErr := validatePort(path.LocalPathPort, "local_path_port"); localPathPortErr != nil {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "expose_config",
Wrapped: resource.ErrInvalidListElement{
Name: "expose_paths",
Index: i,
Wrapped: localPathPortErr,
},
})
}
}
}

return err
}

func validatePort(port uint32, fieldName string) error {
if port < 1 || port > math.MaxUint16 {
return resource.ErrInvalidField{
Name: fieldName,
Wrapped: errInvalidPort,
}
}
return nil
}
Loading

0 comments on commit b08d9d4

Please sign in to comment.