open-consul/agent/structs/config_entry_mesh.go

232 lines
6.2 KiB
Go
Raw Permalink Normal View History

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
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"`
Add x-forwarded-client-cert headers Description Add x-fowarded-client-cert information on trusted incoming connections. Envoy provides support forwarding and annotating the x-forwarded-client-cert header via the forward_client_cert_details set_current_client_cert_details filter fields. It would be helpful for consul to support this directly in its config. The escape hatches are a bit cumbersome for this purpose. This has been implemented on incoming connections to envoy. Outgoing (from the local service through the sidecar) will not have a certificate, and so are left alone. A service on an incoming connection will now get headers something like this: ``` X-Forwarded-Client-Cert:[By=spiffe://efad7282-d9b2-3298-f6d8-38b37fb58df3.consul/ns/default/dc/dc1/svc/counting;Hash=61ad5cbdfcb50f5a3ec0ca60923d61613c149a9d4495010a64175c05a0268ab2;Cert="-----BEGIN%20CERTIFICATE-----%0AMIICHDCCAcOgAwIBAgIBCDAKBggqhkjOPQQDAjAxMS8wLQYDVQQDEyZwcmktMTli%0AYXdyb2YuY29uc3VsLmNhLmVmYWQ3MjgyLmNvbnN1bDAeFw0yMjA0MjkwMzE0NTBa%0AFw0yMjA1MDIwMzE0NTBaMAAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARVIZ7Y%0AZEXfbOGBfxGa7Vuok1MIng%2FuzLQK2xLVlSTIPDbO5hstTGP%2B%2FGx182PYFP3jYqk5%0Aq6rYWe1wiPNMA30Io4H8MIH5MA4GA1UdDwEB%2FwQEAwIDuDAdBgNVHSUEFjAUBggr%0ABgEFBQcDAgYIKwYBBQUHAwEwDAYDVR0TAQH%2FBAIwADApBgNVHQ4EIgQgrp4q50oX%0AHHghMbxz5Bk8OJFWMdfgH0Upr350WlhyxvkwKwYDVR0jBCQwIoAgUe6uERAIj%2FLM%0AyuFzDc3Wbp9TGAKBJYAwyhF14ToOQCMwYgYDVR0RAQH%2FBFgwVoZUc3BpZmZlOi8v%0AZWZhZDcyODItZDliMi0zMjk4LWY2ZDgtMzhiMzdmYjU4ZGYzLmNvbnN1bC9ucy9k%0AZWZhdWx0L2RjL2RjMS9zdmMvZGFzaGJvYXJkMAoGCCqGSM49BAMCA0cAMEQCIDwb%0AFlchufggNTijnQ5SUcvTZrWlZyq%2FrdVC20nbbmWLAiAVshNNv1xBqJI1NmY2HI9n%0AgRMfb8aEPVSuxEHhqy57eQ%3D%3D%0A-----END%20CERTIFICATE-----%0A";Chain="-----BEGIN%20CERTIFICATE-----%0AMIICHDCCAcOgAwIBAgIBCDAKBggqhkjOPQQDAjAxMS8wLQYDVQQDEyZwcmktMTli%0AYXdyb2YuY29uc3VsLmNhLmVmYWQ3MjgyLmNvbnN1bDAeFw0yMjA0MjkwMzE0NTBa%0AFw0yMjA1MDIwMzE0NTBaMAAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARVIZ7Y%0AZEXfbOGBfxGa7Vuok1MIng%2FuzLQK2xLVlSTIPDbO5hstTGP%2B%2FGx182PYFP3jYqk5%0Aq6rYWe1wiPNMA30Io4H8MIH5MA4GA1UdDwEB%2FwQEAwIDuDAdBgNVHSUEFjAUBggr%0ABgEFBQcDAgYIKwYBBQUHAwEwDAYDVR0TAQH%2FBAIwADApBgNVHQ4EIgQgrp4q50oX%0AHHghMbxz5Bk8OJFWMdfgH0Upr350WlhyxvkwKwYDVR0jBCQwIoAgUe6uERAIj%2FLM%0AyuFzDc3Wbp9TGAKBJYAwyhF14ToOQCMwYgYDVR0RAQH%2FBFgwVoZUc3BpZmZlOi8v%0AZWZhZDcyODItZDliMi0zMjk4LWY2ZDgtMzhiMzdmYjU4ZGYzLmNvbnN1bC9ucy9k%0AZWZhdWx0L2RjL2RjMS9zdmMvZGFzaGJvYXJkMAoGCCqGSM49BAMCA0cAMEQCIDwb%0AFlchufggNTijnQ5SUcvTZrWlZyq%2FrdVC20nbbmWLAiAVshNNv1xBqJI1NmY2HI9n%0AgRMfb8aEPVSuxEHhqy57eQ%3D%3D%0A-----END%20CERTIFICATE-----%0A";Subject="";URI=spiffe://efad7282-d9b2-3298-f6d8-38b37fb58df3.consul/ns/default/dc/dc1/svc/dashboard] ``` Closes #12852
2022-04-26 20:46:29 +00:00
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
}