Skip to content

Commit

Permalink
mesh: add validation hook to proxy configuration (#19186)
Browse files Browse the repository at this point in the history
  • Loading branch information
ishustava authored Oct 13, 2023
1 parent 9b0f4b7 commit e94d6ce
Show file tree
Hide file tree
Showing 9 changed files with 555 additions and 195 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
14 changes: 14 additions & 0 deletions internal/mesh/internal/types/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

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")
)
174 changes: 164 additions & 10 deletions internal/mesh/internal/types/proxy_configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
package types

import (
"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"
"github.com/hashicorp/consul/proto-public/pbresource"
Expand Down Expand Up @@ -53,23 +56,174 @@ 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,
})
}

return merr
if cfg.GetAccessLogs() != nil {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "access_logs",
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,
},
})
}

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 e94d6ce

Please sign in to comment.