// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package structs import ( "encoding/json" "fmt" "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/types" ) type MeshConfigEntry struct { // TransparentProxy contains cluster-wide options pertaining to TPROXY mode // when enabled. TransparentProxy TransparentProxyMeshConfig `alias:"transparent_proxy"` // AllowEnablingPermissiveMutualTLS must be true in order to allow setting // MutualTLSMode=permissive in either service-defaults or proxy-defaults. AllowEnablingPermissiveMutualTLS bool `json:",omitempty" alias:"allow_enabling_permissive_mutual_tls"` TLS *MeshTLSConfig `json:",omitempty"` HTTP *MeshHTTPConfig `json:",omitempty"` Peering *PeeringMeshConfig `json:",omitempty"` Meta map[string]string `json:",omitempty"` Hash uint64 `json:",omitempty" hash:"ignore"` acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"` RaftIndex `hash:"ignore"` } func (e *MeshConfigEntry) SetHash(h uint64) { e.Hash = h } func (e *MeshConfigEntry) GetHash() uint64 { return e.Hash } // TransparentProxyMeshConfig contains cluster-wide options pertaining to // TPROXY mode when enabled. type TransparentProxyMeshConfig struct { // MeshDestinationsOnly can be used to disable the pass-through that // allows traffic to destinations outside of the mesh. MeshDestinationsOnly bool `alias:"mesh_destinations_only"` } type MeshTLSConfig struct { Incoming *MeshDirectionalTLSConfig `json:",omitempty"` Outgoing *MeshDirectionalTLSConfig `json:",omitempty"` } type MeshDirectionalTLSConfig struct { TLSMinVersion types.TLSVersion `json:",omitempty" alias:"tls_min_version"` TLSMaxVersion types.TLSVersion `json:",omitempty" alias:"tls_max_version"` // Define a subset of cipher suites to restrict // Only applicable to connections negotiated via TLS 1.2 or earlier CipherSuites []types.TLSCipherSuite `json:",omitempty" alias:"cipher_suites"` } type MeshHTTPConfig struct { SanitizeXForwardedClientCert bool `alias:"sanitize_x_forwarded_client_cert"` } // PeeringMeshConfig contains cluster-wide options pertaining to peering. type PeeringMeshConfig struct { // PeerThroughMeshGateways determines whether peering traffic between // control planes should flow through mesh gateways. If enabled, // Consul servers will advertise mesh gateway addresses as their own. // Additionally, mesh gateways will configure themselves to expose // the local servers using a peering-specific SNI. PeerThroughMeshGateways bool `alias:"peer_through_mesh_gateways"` } func (e *MeshConfigEntry) GetKind() string { return MeshConfig } func (e *MeshConfigEntry) GetName() string { if e == nil { return "" } return MeshConfigMesh } func (e *MeshConfigEntry) GetMeta() map[string]string { if e == nil { return nil } return e.Meta } func (e *MeshConfigEntry) Normalize() error { if e == nil { return fmt.Errorf("config entry is nil") } e.EnterpriseMeta.Normalize() h, err := HashConfigEntry(e) if err != nil { return err } e.Hash = h return nil } func (e *MeshConfigEntry) Validate() error { if e == nil { return fmt.Errorf("config entry is nil") } if err := validateConfigEntryMeta(e.Meta); err != nil { return err } if e.TLS != nil { if e.TLS.Incoming != nil { if err := validateMeshDirectionalTLSConfig(e.TLS.Incoming); err != nil { return fmt.Errorf("error in incoming TLS configuration: %v", err) } } if e.TLS.Outgoing != nil { if err := validateMeshDirectionalTLSConfig(e.TLS.Outgoing); err != nil { return fmt.Errorf("error in outgoing TLS configuration: %v", err) } } } return e.validateEnterpriseMeta() } func (e *MeshConfigEntry) CanRead(authz acl.Authorizer) error { return nil } func (e *MeshConfigEntry) CanWrite(authz acl.Authorizer) error { var authzContext acl.AuthorizerContext e.FillAuthzContext(&authzContext) return authz.ToAllowAuthorizer().MeshWriteAllowed(&authzContext) } func (e *MeshConfigEntry) GetRaftIndex() *RaftIndex { if e == nil { return &RaftIndex{} } return &e.RaftIndex } func (e *MeshConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta { if e == nil { return nil } return &e.EnterpriseMeta } // MarshalJSON adds the Kind field so that the JSON can be decoded back into the // correct type. // This method is implemented on the structs type (as apposed to the api type) // because that is what the API currently uses to return a response. func (e *MeshConfigEntry) MarshalJSON() ([]byte, error) { type Alias MeshConfigEntry source := &struct { Kind string *Alias }{ Kind: MeshConfig, Alias: (*Alias)(e), } return json.Marshal(source) } func (e *MeshConfigEntry) PeerThroughMeshGateways() bool { if e == nil || e.Peering == nil { return false } return e.Peering.PeerThroughMeshGateways } func validateMeshDirectionalTLSConfig(cfg *MeshDirectionalTLSConfig) error { if cfg == nil { return nil } return validateTLSConfig(cfg.TLSMinVersion, cfg.TLSMaxVersion, cfg.CipherSuites) } func validateTLSConfig( tlsMinVersion types.TLSVersion, tlsMaxVersion types.TLSVersion, cipherSuites []types.TLSCipherSuite, ) error { if tlsMinVersion != types.TLSVersionUnspecified { if err := types.ValidateTLSVersion(tlsMinVersion); err != nil { return err } } if tlsMaxVersion != types.TLSVersionUnspecified { if err := types.ValidateTLSVersion(tlsMaxVersion); err != nil { return err } if tlsMinVersion != types.TLSVersionUnspecified { if err, maxLessThanMin := tlsMaxVersion.LessThan(tlsMinVersion); err == nil && maxLessThanMin { return fmt.Errorf("configuring max version %s less than the configured min version %s is invalid", tlsMaxVersion, tlsMinVersion) } } } if len(cipherSuites) != 0 { if _, ok := types.TLSVersionsWithConfigurableCipherSuites[tlsMinVersion]; !ok { return fmt.Errorf("configuring CipherSuites is only applicable to connections negotiated with TLS 1.2 or earlier, TLSMinVersion is set to %s", tlsMinVersion) } // NOTE: it would be nice to emit a warning but not return an error from // here if TLSMaxVersion is unspecified, TLS_AUTO or TLSv1_3 if err := types.ValidateEnvoyCipherSuites(cipherSuites); err != nil { return err } } return nil }