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 committed Oct 13, 2023
1 parent dbca544 commit a388d86
Show file tree
Hide file tree
Showing 13 changed files with 425 additions and 14 deletions.
4 changes: 4 additions & 0 deletions internal/catalog/exports.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,7 @@ func NewFailoverPolicyMapper() FailoverPolicyMapper {
func ValidateLocalServiceRefNoSection(ref *pbresource.Reference, wrapErr func(error) error) error {
return types.ValidateLocalServiceRefNoSection(ref, wrapErr)
}

func ValidateSelector(sel *pbcatalog.WorkloadSelector, allowEmpty bool) error {
return types.ValidateSelector(sel, allowEmpty)
}
2 changes: 1 addition & 1 deletion internal/catalog/internal/types/dns_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func ValidateDNSPolicy(res *pbresource.Resource) error {
var err error
// Ensure that this resource isn't useless and is attempting to
// select at least one workload.
if selErr := validateSelector(policy.Workloads, false); selErr != nil {
if selErr := ValidateSelector(policy.Workloads, false); selErr != nil {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "workloads",
Wrapped: selErr,
Expand Down
2 changes: 1 addition & 1 deletion internal/catalog/internal/types/health_checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func ValidateHealthChecks(res *pbresource.Resource) error {
var err error

// Validate the workload selector
if selErr := validateSelector(checks.Workloads, false); selErr != nil {
if selErr := ValidateSelector(checks.Workloads, false); selErr != nil {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "workloads",
Wrapped: selErr,
Expand Down
2 changes: 1 addition & 1 deletion internal/catalog/internal/types/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func ValidateService(res *pbresource.Resource) error {
// ServiceEndpoints objects for this service such as when desiring to
// configure endpoint information for external services that are not
// registered as workloads
if selErr := validateSelector(service.Workloads, true); selErr != nil {
if selErr := ValidateSelector(service.Workloads, true); selErr != nil {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "workloads",
Wrapped: selErr,
Expand Down
2 changes: 1 addition & 1 deletion internal/catalog/internal/types/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func validateWorkloadHost(host string) error {
return nil
}

func validateSelector(sel *pbcatalog.WorkloadSelector, allowEmpty bool) error {
func ValidateSelector(sel *pbcatalog.WorkloadSelector, allowEmpty bool) error {
if sel == nil {
if allowEmpty {
return nil
Expand Down
2 changes: 1 addition & 1 deletion internal/catalog/internal/types/validators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ func TestValidateSelector(t *testing.T) {

for name, tcase := range cases {
t.Run(name, func(t *testing.T) {
err := validateSelector(tcase.selector, tcase.allowEmpty)
err := ValidateSelector(tcase.selector, tcase.allowEmpty)
if tcase.err == nil {
require.NoError(t, err)
} else {
Expand Down
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")
)
194 changes: 189 additions & 5 deletions internal/mesh/internal/types/proxy_configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
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 All @@ -12,11 +17,10 @@ import (

func RegisterProxyConfiguration(r resource.Registry) {
r.Register(resource.Registration{
Type: pbmesh.ProxyConfigurationType,
Proto: &pbmesh.ProxyConfiguration{},
Scope: resource.ScopeNamespace,
// TODO(rb): add validation for proxy configuration
Validate: nil,
Type: pbmesh.ProxyConfigurationType,
Proto: &pbmesh.ProxyConfiguration{},
Scope: resource.ScopeNamespace,
Validate: ValidateProxyConfiguration,
Mutate: MutateProxyConfiguration,
})
}
Expand Down Expand Up @@ -49,3 +53,183 @@ func MutateProxyConfiguration(res *pbresource.Resource) error {

return res.Data.MarshalFrom(&proxyCfg)
}

func ValidateProxyConfiguration(res *pbresource.Resource) error {
decodedProxyCfg, decodeErr := resource.Decode[*pbmesh.ProxyConfiguration](res)
if decodeErr != nil {
return resource.NewErrDataParse(decodedProxyCfg.GetData(), decodeErr)
}
proxyCfg := decodedProxyCfg.GetData()

var err error

if selErr := catalog.ValidateSelector(proxyCfg.Workloads, false); selErr != nil {
err = multierror.Append(err, resource.ErrInvalidField{
Name: "workloads",
Wrapped: selErr,
})
}

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,
},
})
}

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 a388d86

Please sign in to comment.