jobspec: update gateway.ingress.service Consul API fields (#20176)

Add support for further configuring `gateway.ingress.service` blocks to bring
this block up-to-date with currently available Consul API fields (except for
namespace and admin partition, which will need be handled under a different
PR). These fields are sent to Consul as part of the job endpoint submission hook
for Connect gateways.

Co-authored-by: Horacio Monsalvo <horacio.monsalvo@southworks.com>
This commit is contained in:
Tim Gross
2024-03-22 13:50:48 -04:00
committed by GitHub
parent 2556ff9a0e
commit 10dd738a03
13 changed files with 930 additions and 101 deletions

3
.changelog/16753.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:improvement
consul/connect: Added support for TLS configuration, headers configuration, and request limit configuration to ingress service block
```

View File

@@ -4,6 +4,7 @@
package api package api
import ( import (
"slices"
"time" "time"
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
@@ -400,12 +401,52 @@ func (p *ConsulGatewayProxy) Copy() *ConsulGatewayProxy {
} }
} }
// ConsulGatewayTLSConfig is used to configure TLS for a gateway. // ConsulGatewayTLSSDSConfig is used to configure the gateway's TLS listener to
// load certificates from an external Secret Discovery Service (SDS)
type ConsulGatewayTLSSDSConfig struct {
// ClusterName specifies the name of the SDS cluster where Consul should
// retrieve certificates.
ClusterName string `hcl:"cluster_name,optional" mapstructure:"cluster_name"`
// CertResource specifies an SDS resource name
CertResource string `hcl:"cert_resource,optional" mapstructure:"cert_resource"`
}
func (c *ConsulGatewayTLSSDSConfig) Copy() *ConsulGatewayTLSSDSConfig {
if c == nil {
return nil
}
return &ConsulGatewayTLSSDSConfig{
ClusterName: c.ClusterName,
CertResource: c.CertResource,
}
}
// ConsulGatewayTLSConfig is used to configure TLS for a gateway. Both
// ConsulIngressConfigEntry and ConsulIngressService use this struct. For more
// details, consult the Consul documentation:
// https://developer.hashicorp.com/consul/docs/connect/config-entries/ingress-gateway#listeners-services-tls
type ConsulGatewayTLSConfig struct { type ConsulGatewayTLSConfig struct {
Enabled bool `hcl:"enabled,optional"`
TLSMinVersion string `hcl:"tls_min_version,optional" mapstructure:"tls_min_version"` // Enabled indicates whether TLS is enabled for the configuration entry
TLSMaxVersion string `hcl:"tls_max_version,optional" mapstructure:"tls_max_version"` Enabled bool `hcl:"enabled,optional"`
CipherSuites []string `hcl:"cipher_suites,optional" mapstructure:"cipher_suites"`
// TLSMinVersion specifies the minimum TLS version supported for gateway
// listeners.
TLSMinVersion string `hcl:"tls_min_version,optional" mapstructure:"tls_min_version"`
// TLSMaxVersion specifies the maxmimum TLS version supported for gateway
// listeners.
TLSMaxVersion string `hcl:"tls_max_version,optional" mapstructure:"tls_max_version"`
// CipherSuites specifies a list of cipher suites that gateway listeners
// support when negotiating connections using TLS 1.2 or older.
CipherSuites []string `hcl:"cipher_suites,optional" mapstructure:"cipher_suites"`
// SDS specifies parameters that configure the listener to load TLS
// certificates from an external Secrets Discovery Service (SDS).
SDS *ConsulGatewayTLSSDSConfig `hcl:"sds,block" mapstructure:"sds"`
} }
func (tc *ConsulGatewayTLSConfig) Canonicalize() { func (tc *ConsulGatewayTLSConfig) Canonicalize() {
@@ -420,6 +461,7 @@ func (tc *ConsulGatewayTLSConfig) Copy() *ConsulGatewayTLSConfig {
Enabled: tc.Enabled, Enabled: tc.Enabled,
TLSMinVersion: tc.TLSMinVersion, TLSMinVersion: tc.TLSMinVersion,
TLSMaxVersion: tc.TLSMaxVersion, TLSMaxVersion: tc.TLSMaxVersion,
SDS: tc.SDS.Copy(),
} }
if len(tc.CipherSuites) != 0 { if len(tc.CipherSuites) != 0 {
cipherSuites := make([]string, len(tc.CipherSuites)) cipherSuites := make([]string, len(tc.CipherSuites))
@@ -430,13 +472,90 @@ func (tc *ConsulGatewayTLSConfig) Copy() *ConsulGatewayTLSConfig {
return result return result
} }
// ConsulIngressService is used to configure a service fronted by the ingress gateway. // ConsulHTTPHeaderModifiers is a set of rules for HTTP header modification that
// should be performed by proxies as the request passes through them. It can
// operate on either request or response headers depending on the context in
// which it is used.
type ConsulHTTPHeaderModifiers struct {
// Add is a set of name -> value pairs that should be appended to the
// request or response (i.e. allowing duplicates if the same header already
// exists).
Add map[string]string `hcl:"add,block" mapstructure:"add"`
// Set is a set of name -> value pairs that should be added to the request
// or response, overwriting any existing header values of the same name.
Set map[string]string `hcl:"set,block" mapstructure:"set"`
// Remove is the set of header names that should be stripped from the
// request or response.
Remove []string `hcl:"remove,optional" mapstructure:"remove"`
}
func (h *ConsulHTTPHeaderModifiers) Copy() *ConsulHTTPHeaderModifiers {
if h == nil {
return nil
}
return &ConsulHTTPHeaderModifiers{
Add: maps.Clone(h.Add),
Set: maps.Clone(h.Set),
Remove: slices.Clone(h.Remove),
}
}
func (h *ConsulHTTPHeaderModifiers) Canonicalize() {
if h == nil {
return
}
if len(h.Add) == 0 {
h.Add = nil
}
if len(h.Set) == 0 {
h.Set = nil
}
if len(h.Remove) == 0 {
h.Remove = nil
}
}
// ConsulIngressService is used to configure a service fronted by the ingress
// gateway. For more details, consult the Consul documentation:
// https://developer.hashicorp.com/consul/docs/connect/config-entries/ingress-gateway
type ConsulIngressService struct { type ConsulIngressService struct {
// Namespace is not yet supported. // Namespace is not yet supported.
// Namespace string // Namespace string
// Name of the service exposed through this listener.
Name string `hcl:"name,optional"` Name string `hcl:"name,optional"`
// Hosts specifies one or more hosts that the listening services can receive
// requests on.
Hosts []string `hcl:"hosts,optional"` Hosts []string `hcl:"hosts,optional"`
// TLS specifies a TLS configuration override for a specific service. If
// unset this will fallback to the ConsulIngressConfigEntry's own TLS field.
TLS *ConsulGatewayTLSConfig `hcl:"tls,block" mapstructure:"tls"`
// RequestHeaders specifies a set of HTTP-specific header modification rules
// applied to requests routed through the gateway
RequestHeaders *ConsulHTTPHeaderModifiers `hcl:"request_headers,block" mapstructure:"request_headers"`
// ResponseHeader specifies a set of HTTP-specific header modification rules
// applied to responses routed through the gateway
ResponseHeaders *ConsulHTTPHeaderModifiers `hcl:"response_headers,block" mapstructure:"response_headers"`
// MaxConnections specifies the maximum number of HTTP/1.1 connections a
// service instance is allowed to establish against the upstream
MaxConnections *uint32 `hcl:"max_connections,optional" mapstructure:"max_connections"`
// MaxPendingRequests specifies the maximum number of requests that are
// allowed to queue while waiting to establish a connection
MaxPendingRequests *uint32 `hcl:"max_pending_requests,optional" mapstructure:"max_pending_requests"`
// MaxConcurrentRequests specifies the maximum number of concurrent HTTP/2
// traffic requests that are allowed at a single point in time
MaxConcurrentRequests *uint32 `hcl:"max_concurrent_requests,optional" mapstructure:"max_concurrent_requests"`
} }
func (s *ConsulIngressService) Canonicalize() { func (s *ConsulIngressService) Canonicalize() {
@@ -447,6 +566,9 @@ func (s *ConsulIngressService) Canonicalize() {
if len(s.Hosts) == 0 { if len(s.Hosts) == 0 {
s.Hosts = nil s.Hosts = nil
} }
s.RequestHeaders.Canonicalize()
s.ResponseHeaders.Canonicalize()
} }
func (s *ConsulIngressService) Copy() *ConsulIngressService { func (s *ConsulIngressService) Copy() *ConsulIngressService {
@@ -454,16 +576,19 @@ func (s *ConsulIngressService) Copy() *ConsulIngressService {
return nil return nil
} }
var hosts []string = nil ns := new(ConsulIngressService)
if n := len(s.Hosts); n > 0 { *ns = *s
hosts = make([]string, n)
copy(hosts, s.Hosts)
}
return &ConsulIngressService{ ns.Hosts = slices.Clone(s.Hosts)
Name: s.Name, ns.RequestHeaders = s.RequestHeaders.Copy()
Hosts: hosts, ns.ResponseHeaders = s.ResponseHeaders.Copy()
} ns.TLS = s.TLS.Copy()
ns.MaxConnections = pointerCopy(s.MaxConnections)
ns.MaxPendingRequests = pointerCopy(s.MaxPendingRequests)
ns.MaxConcurrentRequests = pointerCopy(s.MaxConcurrentRequests)
return ns
} }
const ( const (
@@ -521,7 +646,11 @@ type ConsulIngressConfigEntry struct {
// Namespace is not yet supported. // Namespace is not yet supported.
// Namespace string // Namespace string
TLS *ConsulGatewayTLSConfig `hcl:"tls,block"` // TLS specifies a TLS configuration for the gateway.
TLS *ConsulGatewayTLSConfig `hcl:"tls,block"`
// Listeners specifies a list of listeners in the mesh for the
// gateway. Listeners are uniquely identified by their port number.
Listeners []*ConsulIngressListener `hcl:"listener,block"` Listeners []*ConsulIngressListener `hcl:"listener,block"`
} }

View File

@@ -415,6 +415,33 @@ func TestConsulIngressConfigEntry_Copy(t *testing.T) {
Services: []*ConsulIngressService{{ Services: []*ConsulIngressService{{
Name: "service1", Name: "service1",
Hosts: []string{"1.1.1.1", "1.1.1.1:9000"}, Hosts: []string{"1.1.1.1", "1.1.1.1:9000"},
TLS: &ConsulGatewayTLSConfig{
SDS: &ConsulGatewayTLSSDSConfig{
ClusterName: "foo",
CertResource: "bar",
},
},
RequestHeaders: &ConsulHTTPHeaderModifiers{
Add: map[string]string{
"test": "testvalue",
},
Set: map[string]string{
"test1": "testvalue1",
},
Remove: []string{"test2"},
},
ResponseHeaders: &ConsulHTTPHeaderModifiers{
Add: map[string]string{
"test": "testvalue",
},
Set: map[string]string{
"test1": "testvalue1",
},
Remove: []string{"test2"},
},
MaxConnections: pointerOf(uint32(5120)),
MaxPendingRequests: pointerOf(uint32(512)),
MaxConcurrentRequests: pointerOf(uint32(2048)),
}, { }, {
Name: "service2", Name: "service2",
Hosts: []string{"2.2.2.2"}, Hosts: []string{"2.2.2.2"},

View File

@@ -33,3 +33,12 @@ func formatFloat(f float64, maxPrec int) string {
func pointerOf[A any](a A) *A { func pointerOf[A any](a A) *A {
return &a return &a
} }
// pointerCopy returns a new pointer to a.
func pointerCopy[A any](a *A) *A {
if a == nil {
return nil
}
na := *a
return &na
}

View File

@@ -1745,6 +1745,18 @@ func apiConnectGatewayTLSConfig(in *api.ConsulGatewayTLSConfig) *structs.ConsulG
TLSMinVersion: in.TLSMinVersion, TLSMinVersion: in.TLSMinVersion,
TLSMaxVersion: in.TLSMaxVersion, TLSMaxVersion: in.TLSMaxVersion,
CipherSuites: slices.Clone(in.CipherSuites), CipherSuites: slices.Clone(in.CipherSuites),
SDS: apiConnectGatewayTLSSDSConfig(in.SDS),
}
}
func apiConnectGatewayTLSSDSConfig(in *api.ConsulGatewayTLSSDSConfig) *structs.ConsulGatewayTLSSDSConfig {
if in == nil {
return nil
}
return &structs.ConsulGatewayTLSSDSConfig{
ClusterName: in.ClusterName,
CertResource: in.CertResource,
} }
} }
@@ -1790,8 +1802,26 @@ func apiConnectIngressServiceToStructs(in *api.ConsulIngressService) *structs.Co
} }
return &structs.ConsulIngressService{ return &structs.ConsulIngressService{
Name: in.Name, Name: in.Name,
Hosts: slices.Clone(in.Hosts), Hosts: slices.Clone(in.Hosts),
TLS: apiConnectGatewayTLSConfig(in.TLS),
RequestHeaders: apiConsulHTTPHeaderModifiersToStructs(in.RequestHeaders),
ResponseHeaders: apiConsulHTTPHeaderModifiersToStructs(in.ResponseHeaders),
MaxConnections: in.MaxConnections,
MaxPendingRequests: in.MaxPendingRequests,
MaxConcurrentRequests: in.MaxConcurrentRequests,
}
}
func apiConsulHTTPHeaderModifiersToStructs(in *api.ConsulHTTPHeaderModifiers) *structs.ConsulHTTPHeaderModifiers {
if in == nil {
return nil
}
return &structs.ConsulHTTPHeaderModifiers{
Add: maps.Clone(in.Add),
Set: maps.Clone(in.Set),
Remove: slices.Clone(in.Remove),
} }
} }

View File

@@ -4182,6 +4182,33 @@ func TestConversion_ApiConsulConnectToStructs(t *testing.T) {
Services: []*structs.ConsulIngressService{{ Services: []*structs.ConsulIngressService{{
Name: "ingress1", Name: "ingress1",
Hosts: []string{"host1"}, Hosts: []string{"host1"},
TLS: &structs.ConsulGatewayTLSConfig{
SDS: &structs.ConsulGatewayTLSSDSConfig{
ClusterName: "foo",
CertResource: "bar",
},
},
RequestHeaders: &structs.ConsulHTTPHeaderModifiers{
Add: map[string]string{
"test": "testvalue",
},
Set: map[string]string{
"test1": "testvalue1",
},
Remove: []string{"test2"},
},
ResponseHeaders: &structs.ConsulHTTPHeaderModifiers{
Add: map[string]string{
"test": "testvalue",
},
Set: map[string]string{
"test1": "testvalue1",
},
Remove: []string{"test2"},
},
MaxConnections: pointer.Of(uint32(5120)),
MaxPendingRequests: pointer.Of(uint32(512)),
MaxConcurrentRequests: pointer.Of(uint32(2048)),
}}, }},
}}, }},
}, },
@@ -4202,6 +4229,33 @@ func TestConversion_ApiConsulConnectToStructs(t *testing.T) {
Services: []*api.ConsulIngressService{{ Services: []*api.ConsulIngressService{{
Name: "ingress1", Name: "ingress1",
Hosts: []string{"host1"}, Hosts: []string{"host1"},
TLS: &api.ConsulGatewayTLSConfig{
SDS: &api.ConsulGatewayTLSSDSConfig{
ClusterName: "foo",
CertResource: "bar",
},
},
RequestHeaders: &api.ConsulHTTPHeaderModifiers{
Add: map[string]string{
"test": "testvalue",
},
Set: map[string]string{
"test1": "testvalue1",
},
Remove: []string{"test2"},
},
ResponseHeaders: &api.ConsulHTTPHeaderModifiers{
Add: map[string]string{
"test": "testvalue",
},
Set: map[string]string{
"test1": "testvalue1",
},
Remove: []string{"test2"},
},
MaxConnections: pointer.Of(uint32(5120)),
MaxPendingRequests: pointer.Of(uint32(512)),
MaxConcurrentRequests: pointer.Of(uint32(2048)),
}}, }},
}}, }},
}, },

View File

@@ -425,10 +425,84 @@ func parseGatewayProxy(o *ast.ObjectItem) (*api.ConsulGatewayProxy, error) {
return &proxy, nil return &proxy, nil
} }
func parseConsulHTTPHeaderModifiers(o *ast.ObjectItem) (*api.ConsulHTTPHeaderModifiers, error) {
valid := []string{
"add",
"set",
"remove",
}
if err := checkHCLKeys(o.Val, valid); err != nil {
return nil, multierror.Prefix(err, "httpHeaderModifiers ->")
}
var httpHeaderModifiers api.ConsulHTTPHeaderModifiers
var m map[string]interface{}
if err := hcl.DecodeObject(&m, o.Val); err != nil {
return nil, err
}
delete(m, "add")
delete(m, "set")
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Result: &httpHeaderModifiers,
})
if err != nil {
return nil, err
}
if err := dec.Decode(m); err != nil {
return nil, err
}
// Filter list
var listVal *ast.ObjectList
if ot, ok := o.Val.(*ast.ObjectType); ok {
listVal = ot.List
} else {
return nil, fmt.Errorf("'httpHeaderModifiers: should be an object")
}
// Parse Add
if addO := listVal.Filter("add"); len(addO.Items) > 0 {
for _, o := range addO.Elem().Items {
var m map[string]interface{}
if err := hcl.DecodeObject(&m, o.Val); err != nil {
return nil, err
}
if err := mapstructure.WeakDecode(m, &httpHeaderModifiers.Add); err != nil {
return nil, err
}
}
}
// Parse Set
if setO := listVal.Filter("set"); len(setO.Items) > 0 {
for _, o := range setO.Elem().Items {
var m map[string]interface{}
if err := hcl.DecodeObject(&m, o.Val); err != nil {
return nil, err
}
if err := mapstructure.WeakDecode(m, &httpHeaderModifiers.Set); err != nil {
return nil, err
}
}
}
return &httpHeaderModifiers, nil
}
func parseConsulIngressService(o *ast.ObjectItem) (*api.ConsulIngressService, error) { func parseConsulIngressService(o *ast.ObjectItem) (*api.ConsulIngressService, error) {
valid := []string{ valid := []string{
"name", "name",
"hosts", "hosts",
"tls",
"request_headers",
"response_headers",
"max_connections",
"max_pending_requests",
"max_concurrent_requests",
} }
if err := checkHCLKeys(o.Val, valid); err != nil { if err := checkHCLKeys(o.Val, valid); err != nil {
@@ -441,6 +515,10 @@ func parseConsulIngressService(o *ast.ObjectItem) (*api.ConsulIngressService, er
return nil, err return nil, err
} }
delete(m, "tls")
delete(m, "request_headers")
delete(m, "response_headers")
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Result: &service, Result: &service,
}) })
@@ -452,6 +530,37 @@ func parseConsulIngressService(o *ast.ObjectItem) (*api.ConsulIngressService, er
return nil, err return nil, err
} }
var listVal *ast.ObjectList
if ot, ok := o.Val.(*ast.ObjectType); ok {
listVal = ot.List
} else {
return nil, fmt.Errorf("service: should be an object")
}
// Parse TLS
if tlsO := listVal.Filter("tls"); len(tlsO.Items) > 0 {
service.TLS, err = parseConsulGatewayTLS(tlsO.Items[0])
if err != nil {
return nil, err
}
}
// Parse Request Headers
if rqstHO := listVal.Filter("request_headers"); len(rqstHO.Items) > 0 {
service.RequestHeaders, err = parseConsulHTTPHeaderModifiers(rqstHO.Items[0])
if err != nil {
return nil, err
}
}
// Parse Response Headers
if rspHO := listVal.Filter("response_headers"); len(rspHO.Items) > 0 {
service.ResponseHeaders, err = parseConsulHTTPHeaderModifiers(rspHO.Items[0])
if err != nil {
return nil, err
}
}
return &service, nil return &service, nil
} }
@@ -541,12 +650,43 @@ func parseConsulIngressListener(o *ast.ObjectItem) (*api.ConsulIngressListener,
return &listener, nil return &listener, nil
} }
func parseConsulGatewayTLSSDS(o *ast.ObjectItem) (*api.ConsulGatewayTLSSDSConfig, error) {
valid := []string{
"cluster_name",
"cert_resource",
}
if err := checkHCLKeys(o.Val, valid); err != nil {
return nil, multierror.Prefix(err, "sds ->")
}
var sds api.ConsulGatewayTLSSDSConfig
var m map[string]interface{}
if err := hcl.DecodeObject(&m, o.Val); err != nil {
return nil, err
}
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Result: &sds,
})
if err != nil {
return nil, err
}
if err := dec.Decode(m); err != nil {
return nil, err
}
return &sds, nil
}
func parseConsulGatewayTLS(o *ast.ObjectItem) (*api.ConsulGatewayTLSConfig, error) { func parseConsulGatewayTLS(o *ast.ObjectItem) (*api.ConsulGatewayTLSConfig, error) {
valid := []string{ valid := []string{
"enabled", "enabled",
"tls_min_version", "tls_min_version",
"tls_max_version", "tls_max_version",
"cipher_suites", "cipher_suites",
"sds",
} }
if err := checkHCLKeys(o.Val, valid); err != nil { if err := checkHCLKeys(o.Val, valid); err != nil {
@@ -559,6 +699,8 @@ func parseConsulGatewayTLS(o *ast.ObjectItem) (*api.ConsulGatewayTLSConfig, erro
return nil, err return nil, err
} }
delete(m, "sds")
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Result: &tls, Result: &tls,
}) })
@@ -570,6 +712,22 @@ func parseConsulGatewayTLS(o *ast.ObjectItem) (*api.ConsulGatewayTLSConfig, erro
return nil, err return nil, err
} }
// Parse SDS
var listVal *ast.ObjectList
if ot, ok := o.Val.(*ast.ObjectType); ok {
listVal = ot.List
} else {
return nil, fmt.Errorf("tls: should be an object")
}
so := listVal.Filter("sds")
if len(so.Items) > 0 {
tls.SDS, err = parseConsulGatewayTLSSDS(so.Items[0])
if err != nil {
return nil, err
}
}
return &tls, nil return &tls, nil
} }

View File

@@ -12,6 +12,7 @@ import (
capi "github.com/hashicorp/consul/api" capi "github.com/hashicorp/consul/api"
"github.com/hashicorp/nomad/api" "github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/ci" "github.com/hashicorp/nomad/ci"
"github.com/hashicorp/nomad/helper/pointer"
"github.com/shoenig/test/must" "github.com/shoenig/test/must"
) )
@@ -1682,6 +1683,23 @@ func TestParse(t *testing.T) {
Hosts: []string{ Hosts: []string{
"2.2.2.2:8080", "2.2.2.2:8080",
}, },
TLS: &api.ConsulGatewayTLSConfig{
SDS: &api.ConsulGatewayTLSSDSConfig{
ClusterName: "foo",
CertResource: "bar",
},
},
RequestHeaders: &api.ConsulHTTPHeaderModifiers{
Add: map[string]string{
"test": "testvalue",
},
},
ResponseHeaders: &api.ConsulHTTPHeaderModifiers{
Remove: []string{"test2"},
},
MaxConnections: pointer.Of(uint32(5120)),
MaxPendingRequests: pointer.Of(uint32(512)),
MaxConcurrentRequests: pointer.Of(uint32(2048)),
}}, }},
}, },
}, },

View File

@@ -50,6 +50,23 @@ job "connect_gateway_ingress" {
service { service {
name = "nginx" name = "nginx"
hosts = ["2.2.2.2:8080"] hosts = ["2.2.2.2:8080"]
tls {
sds {
cluster_name = "foo"
cert_resource = "bar"
}
}
request_headers {
add {
test = "testvalue"
}
}
response_headers {
remove = ["test2"]
}
max_connections = 5120
max_pending_requests = 512
max_concurrent_requests = 2048
} }
} }
} }

View File

@@ -7,6 +7,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"maps"
"slices" "slices"
"strings" "strings"
"sync" "sync"
@@ -587,9 +588,21 @@ func convertIngressCE(namespace, service string, entry *structs.ConsulIngressCon
for _, listener := range entry.Listeners { for _, listener := range entry.Listeners {
var services []api.IngressService = nil var services []api.IngressService = nil
for _, s := range listener.Services { for _, s := range listener.Services {
var sds *api.GatewayTLSSDSConfig = nil
if s.TLS != nil {
sds = convertGatewayTLSSDSConfig(s.TLS.SDS)
}
services = append(services, api.IngressService{ services = append(services, api.IngressService{
Name: s.Name, Name: s.Name,
Hosts: slices.Clone(s.Hosts), Hosts: slices.Clone(s.Hosts),
RequestHeaders: convertHTTPHeaderModifiers(s.RequestHeaders),
ResponseHeaders: convertHTTPHeaderModifiers(s.ResponseHeaders),
MaxConnections: s.MaxConnections,
MaxPendingRequests: s.MaxPendingRequests,
MaxConcurrentRequests: s.MaxConcurrentRequests,
TLS: &api.GatewayServiceTLSConfig{
SDS: sds,
},
}) })
} }
listeners = append(listeners, api.IngressListener{ listeners = append(listeners, api.IngressListener{
@@ -611,11 +624,48 @@ func convertIngressCE(namespace, service string, entry *structs.ConsulIngressCon
Namespace: namespace, Namespace: namespace,
Kind: api.IngressGateway, Kind: api.IngressGateway,
Name: service, Name: service,
TLS: tls, TLS: *convertGatewayTLSConfig(entry.TLS),
Listeners: listeners, Listeners: listeners,
} }
} }
func convertHTTPHeaderModifiers(in *structs.ConsulHTTPHeaderModifiers) *api.HTTPHeaderModifiers {
if in != nil {
return &api.HTTPHeaderModifiers{
Add: maps.Clone(in.Add),
Set: maps.Clone(in.Set),
Remove: slices.Clone(in.Remove),
}
}
return &api.HTTPHeaderModifiers{}
}
func convertGatewayTLSConfig(in *structs.ConsulGatewayTLSConfig) *api.GatewayTLSConfig {
if in != nil {
return &api.GatewayTLSConfig{
Enabled: in.Enabled,
TLSMinVersion: in.TLSMinVersion,
TLSMaxVersion: in.TLSMaxVersion,
CipherSuites: slices.Clone(in.CipherSuites),
SDS: convertGatewayTLSSDSConfig(in.SDS),
}
}
return &api.GatewayTLSConfig{}
}
func convertGatewayTLSSDSConfig(in *structs.ConsulGatewayTLSSDSConfig) *api.GatewayTLSSDSConfig {
if in != nil {
return &api.GatewayTLSSDSConfig{
ClusterName: in.ClusterName,
CertResource: in.CertResource,
}
}
return &api.GatewayTLSSDSConfig{}
}
func convertTerminatingCE(namespace, service string, entry *structs.ConsulTerminatingConfigEntry) api.ConfigEntry { func convertTerminatingCE(namespace, service string, entry *structs.ConsulTerminatingConfigEntry) api.ConfigEntry {
var linked []api.LinkedService = nil var linked []api.LinkedService = nil
for _, s := range entry.Services { for _, s := range entry.Services {

View File

@@ -1314,6 +1314,11 @@ func connectGatewayTLSConfigDiff(prev, next *ConsulGatewayTLSConfig, contextual
// Diff the primitive field. // Diff the primitive field.
diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual)
// Diff SDS object
if sdsDiff := primitiveObjectDiff(prev.SDS, next.SDS, nil, "SDS", contextual); sdsDiff != nil {
diff.Objects = append(diff.Objects, sdsDiff)
}
return diff return diff
} }
@@ -1443,6 +1448,38 @@ func connectGatewayIngressServiceDiff(prev, next *ConsulIngressService, contextu
newPrimitiveFlat = flatmap.Flatten(next, nil, true) newPrimitiveFlat = flatmap.Flatten(next, nil, true)
} }
// Diff pointer types.
if prev != nil {
if prev.MaxConnections != nil {
oldPrimitiveFlat["MaxConnections"] = fmt.Sprintf("%v", *prev.MaxConnections)
}
}
if next != nil {
if next.MaxConnections != nil {
newPrimitiveFlat["MaxConnections"] = fmt.Sprintf("%v", *next.MaxConnections)
}
}
if prev != nil {
if prev.MaxPendingRequests != nil {
oldPrimitiveFlat["MaxPendingRequests"] = fmt.Sprintf("%v", *prev.MaxPendingRequests)
}
}
if next != nil {
if next.MaxPendingRequests != nil {
newPrimitiveFlat["MaxPendingRequests"] = fmt.Sprintf("%v", *next.MaxPendingRequests)
}
}
if prev != nil {
if prev.MaxConcurrentRequests != nil {
oldPrimitiveFlat["MaxConcurrentRequests"] = fmt.Sprintf("%v", *prev.MaxConcurrentRequests)
}
}
if next != nil {
if next.MaxConcurrentRequests != nil {
newPrimitiveFlat["MaxConcurrentRequests"] = fmt.Sprintf("%v", *next.MaxConcurrentRequests)
}
}
// Diff the primitive fields. // Diff the primitive fields.
diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual)
@@ -1451,6 +1488,55 @@ func connectGatewayIngressServiceDiff(prev, next *ConsulIngressService, contextu
diff.Objects = append(diff.Objects, hDiffs) diff.Objects = append(diff.Objects, hDiffs)
} }
// Diff the ConsulGatewayTLSConfig objects.
tlsConfigDiff := connectGatewayTLSConfigDiff(prev.TLS, next.TLS, contextual)
if tlsConfigDiff != nil {
diff.Objects = append(diff.Objects, tlsConfigDiff)
}
// Diff the ConsulHTTPHeaderModifiers objects (RequestHeaders).
reqModifiersDiff := connectGatewayHTTPHeaderModifiersDiff(prev.RequestHeaders, next.RequestHeaders, "RequestHeaders", contextual)
if reqModifiersDiff != nil {
diff.Objects = append(diff.Objects, reqModifiersDiff)
}
// Diff the ConsulHTTPHeaderModifiers objects (ResponseHeaders).
respModifiersDiff := connectGatewayHTTPHeaderModifiersDiff(prev.ResponseHeaders, next.ResponseHeaders, "ResponseHeaders", contextual)
if respModifiersDiff != nil {
diff.Objects = append(diff.Objects, respModifiersDiff)
}
return diff
}
func connectGatewayHTTPHeaderModifiersDiff(prev, next *ConsulHTTPHeaderModifiers, name string, contextual bool) *ObjectDiff {
diff := &ObjectDiff{Type: DiffTypeNone, Name: name}
var oldPrimitiveFlat, newPrimitiveFlat map[string]string
if reflect.DeepEqual(prev, next) {
return nil
} else if prev == nil {
prev = new(ConsulHTTPHeaderModifiers)
diff.Type = DiffTypeAdded
newPrimitiveFlat = flatmap.Flatten(next, nil, true)
} else if next == nil {
next = new(ConsulHTTPHeaderModifiers)
diff.Type = DiffTypeDeleted
oldPrimitiveFlat = flatmap.Flatten(prev, nil, true)
} else {
diff.Type = DiffTypeEdited
oldPrimitiveFlat = flatmap.Flatten(prev, nil, true)
newPrimitiveFlat = flatmap.Flatten(next, nil, true)
}
// Diff the primitive fields.
diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual)
// Diff the Remove Headers.
if rDiffs := stringSetDiff(prev.Remove, next.Remove, "Remove", contextual); rDiffs != nil {
diff.Objects = append(diff.Objects, rDiffs)
}
return diff return diff
} }

View File

@@ -1994,12 +1994,61 @@ func (p *ConsulGatewayProxy) Validate() error {
return nil return nil
} }
// ConsulGatewayTLSConfig is used to configure TLS for a gateway. // ConsulGatewayTLSSDSConfig is used to configure the gateway's TLS listener to
// load certificates from an external Secret Discovery Service (SDS)
type ConsulGatewayTLSSDSConfig struct {
// ClusterName specifies the name of the SDS cluster where Consul should
// retrieve certificates.
ClusterName string
// CertResource specifies an SDS resource name
CertResource string
}
func (c *ConsulGatewayTLSSDSConfig) Copy() *ConsulGatewayTLSSDSConfig {
if c == nil {
return nil
}
return &ConsulGatewayTLSSDSConfig{
ClusterName: c.ClusterName,
CertResource: c.CertResource,
}
}
func (c *ConsulGatewayTLSSDSConfig) Equal(o *ConsulGatewayTLSSDSConfig) bool {
if c == nil || o == nil {
return c == o
}
return c.ClusterName == o.ClusterName &&
c.CertResource == o.CertResource
}
// ConsulGatewayTLSConfig is used to configure TLS for a gateway. Both
// ConsulIngressConfigEntry and ConsulIngressService use this struct. For more
// details, consult the Consul documentation:
// https://developer.hashicorp.com/consul/docs/connect/config-entries/ingress-gateway#listeners-services-tls
type ConsulGatewayTLSConfig struct { type ConsulGatewayTLSConfig struct {
Enabled bool // Enabled indicates whether TLS is enabled for the configuration entry
Enabled bool
// TLSMinVersion specifies the minimum TLS version supported for gateway
// listeners.
TLSMinVersion string TLSMinVersion string
// TLSMaxVersion specifies the maxmimum TLS version supported for gateway
// listeners.
TLSMaxVersion string TLSMaxVersion string
CipherSuites []string
// CipherSuites specifies a list of cipher suites that gateway listeners
// support when negotiating connections using TLS 1.2 or older.
CipherSuites []string
// SDS specifies parameters that configure the listener to load TLS
// certificates from an external Secrets Discovery Service (SDS).
SDS *ConsulGatewayTLSSDSConfig
} }
func (c *ConsulGatewayTLSConfig) Copy() *ConsulGatewayTLSConfig { func (c *ConsulGatewayTLSConfig) Copy() *ConsulGatewayTLSConfig {
@@ -2012,6 +2061,7 @@ func (c *ConsulGatewayTLSConfig) Copy() *ConsulGatewayTLSConfig {
TLSMinVersion: c.TLSMinVersion, TLSMinVersion: c.TLSMinVersion,
TLSMaxVersion: c.TLSMaxVersion, TLSMaxVersion: c.TLSMaxVersion,
CipherSuites: slices.Clone(c.CipherSuites), CipherSuites: slices.Clone(c.CipherSuites),
SDS: c.SDS.Copy(),
} }
} }
@@ -2023,13 +2073,95 @@ func (c *ConsulGatewayTLSConfig) Equal(o *ConsulGatewayTLSConfig) bool {
return c.Enabled == o.Enabled && return c.Enabled == o.Enabled &&
c.TLSMinVersion == o.TLSMinVersion && c.TLSMinVersion == o.TLSMinVersion &&
c.TLSMaxVersion == o.TLSMaxVersion && c.TLSMaxVersion == o.TLSMaxVersion &&
helper.SliceSetEq(c.CipherSuites, o.CipherSuites) helper.SliceSetEq(c.CipherSuites, o.CipherSuites) &&
c.SDS.Equal(o.SDS)
}
// ConsulHTTPHeaderModifiers is a set of rules for HTTP header modification that
// should be performed by proxies as the request passes through them. It can
// operate on either request or response headers depending on the context in
// which it is used.
type ConsulHTTPHeaderModifiers struct {
// Add is a set of name -> value pairs that should be appended to the request
// or response (i.e. allowing duplicates if the same header already exists).
Add map[string]string
// Set is a set of name -> value pairs that should be added to the request or
// response, overwriting any existing header values of the same name.
Set map[string]string
// Remove is the set of header names that should be stripped from the request
// or response.
Remove []string
}
func (h *ConsulHTTPHeaderModifiers) Copy() *ConsulHTTPHeaderModifiers {
if h == nil {
return nil
}
return &ConsulHTTPHeaderModifiers{
Add: maps.Clone(h.Add),
Set: maps.Clone(h.Set),
Remove: slices.Clone(h.Remove),
}
}
func (h *ConsulHTTPHeaderModifiers) Equal(o *ConsulHTTPHeaderModifiers) bool {
if h == nil || o == nil {
return h == o
}
if !maps.Equal(h.Add, o.Add) {
return false
}
if !maps.Equal(h.Set, o.Set) {
return false
}
if !helper.SliceSetEq(h.Remove, o.Remove) {
return false
}
return true
} }
// ConsulIngressService is used to configure a service fronted by the ingress gateway. // ConsulIngressService is used to configure a service fronted by the ingress gateway.
// For more details, consult the Consul documentation:
// https://developer.hashicorp.com/consul/docs/connect/config-entries/ingress-gateway
type ConsulIngressService struct { type ConsulIngressService struct {
Name string
// Name of the service exposed through this listener.
Name string
// Hosts specifies one or more hosts that the listening services can receive
// requests on.
Hosts []string Hosts []string
// TLS specifies a TLS configuration override for a specific service. If
// unset this will fallback to the ConsulIngressConfigEntry's own TLS field.
TLS *ConsulGatewayTLSConfig
// RequestHeaders specifies a set of HTTP-specific header modification rules
// applied to requests routed through the gateway
RequestHeaders *ConsulHTTPHeaderModifiers
// ResponseHeader specifies a set of HTTP-specific header modification rules
// applied to responses routed through the gateway
ResponseHeaders *ConsulHTTPHeaderModifiers
// MaxConnections specifies the maximum number of HTTP/1.1 connections a
// service instance is allowed to establish against the upstream
MaxConnections *uint32
// MaxPendingRequests specifies the maximum number of requests that are
// allowed to queue while waiting to establish a connection
MaxPendingRequests *uint32
// MaxConcurrentRequests specifies the maximum number of concurrent HTTP/2
// traffic requests that are allowed at a single point in time
MaxConcurrentRequests *uint32
} }
func (s *ConsulIngressService) Copy() *ConsulIngressService { func (s *ConsulIngressService) Copy() *ConsulIngressService {
@@ -2037,16 +2169,19 @@ func (s *ConsulIngressService) Copy() *ConsulIngressService {
return nil return nil
} }
var hosts []string = nil ns := new(ConsulIngressService)
if n := len(s.Hosts); n > 0 { *ns = *s
hosts = make([]string, n)
copy(hosts, s.Hosts)
}
return &ConsulIngressService{ ns.Hosts = slices.Clone(s.Hosts)
Name: s.Name, ns.RequestHeaders = s.RequestHeaders.Copy()
Hosts: hosts, ns.ResponseHeaders = s.ResponseHeaders.Copy()
} ns.TLS = s.TLS.Copy()
ns.MaxConnections = pointer.Copy(s.MaxConnections)
ns.MaxPendingRequests = pointer.Copy(s.MaxPendingRequests)
ns.MaxConcurrentRequests = pointer.Copy(s.MaxConcurrentRequests)
return ns
} }
func (s *ConsulIngressService) Equal(o *ConsulIngressService) bool { func (s *ConsulIngressService) Equal(o *ConsulIngressService) bool {
@@ -2058,7 +2193,35 @@ func (s *ConsulIngressService) Equal(o *ConsulIngressService) bool {
return false return false
} }
return helper.SliceSetEq(s.Hosts, o.Hosts) if !helper.SliceSetEq(s.Hosts, o.Hosts) {
return false
}
if !s.TLS.Equal(o.TLS) {
return false
}
if !s.RequestHeaders.Equal(o.RequestHeaders) {
return false
}
if !s.ResponseHeaders.Equal(o.ResponseHeaders) {
return false
}
if !pointer.Eq(s.MaxConnections, o.MaxConnections) {
return false
}
if !pointer.Eq(s.MaxPendingRequests, o.MaxPendingRequests) {
return false
}
if !pointer.Eq(s.MaxConcurrentRequests, o.MaxConcurrentRequests) {
return false
}
return true
} }
func (s *ConsulIngressService) Validate(protocol string) error { func (s *ConsulIngressService) Validate(protocol string) error {
@@ -2171,7 +2334,12 @@ func ingressServicesEqual(a, b []*ConsulIngressService) bool {
// //
// https://www.consul.io/docs/agent/config-entries/ingress-gateway#available-fields // https://www.consul.io/docs/agent/config-entries/ingress-gateway#available-fields
type ConsulIngressConfigEntry struct { type ConsulIngressConfigEntry struct {
TLS *ConsulGatewayTLSConfig
// TLS specifies a TLS configuration for the gateway.
TLS *ConsulGatewayTLSConfig
// Listeners specifies a list of listeners in the mesh for the
// gateway. Listeners are uniquely identified by their port number.
Listeners []*ConsulIngressListener Listeners []*ConsulIngressListener
} }

View File

@@ -10,9 +10,10 @@ description: |-
<Placement groups={['job', 'group', 'service', 'connect', 'gateway']} /> <Placement groups={['job', 'group', 'service', 'connect', 'gateway']} />
The `gateway` block allows configuration of [Consul Connect Gateways](/consul/docs/connect/gateways). Nomad will The `gateway` block allows configuration of [Consul Connect
automatically create the necessary Gateway [Configuration Entry](/consul/docs/agent/config-entries) Gateways](/consul/docs/connect/gateways). Nomad will automatically create the
as well as inject an Envoy proxy task into the Nomad job to serve as the Gateway. necessary Gateway [Configuration Entry](/consul/docs/agent/config-entries) as
well as inject an Envoy proxy task into the Nomad job to serve as the Gateway.
The `gateway` configuration is valid within the context of a `connect` block. The `gateway` configuration is valid within the context of a `connect` block.
Additional information about Gateway configurations can be found in Consul's Additional information about Gateway configurations can be found in Consul's
@@ -48,19 +49,23 @@ Exactly one of `ingress`, `terminating`, or `mesh` must be configured.
### `proxy` Parameters ### `proxy` Parameters
- `connect_timeout` `(string: "5s")` - The amount of time to allow when making upstream - `connect_timeout` `(string: "5s")` - The amount of time to allow when making
connections before timing out. Defaults to 5 seconds. If the upstream service has upstream connections before timing out. Defaults to 5 seconds. If the upstream
the configuration option <code>[connect_timeout_ms]</code> set for the `service-resolver`, that service has the configuration option <code>[connect_timeout_ms]</code> set for
timeout value will take precedence over this gateway proxy option. the `service-resolver`, that timeout value will take precedence over this
- `envoy_gateway_bind_tagged_addresses` `(bool: false)` - Indicates that the gateway gateway proxy option.
services tagged addresses should be bound to listeners in addition to the default
listener address. - `envoy_gateway_bind_tagged_addresses` `(bool: false)` - Indicates that the
- `envoy_gateway_bind_addresses` <code>(map<string|[address]>: nil)</code> - A map of additional addresses to be bound. gateway services tagged addresses should be bound to listeners in addition to
The keys to this map are the same of the listeners to be created and the values are the default listener address.
a map with two keys - address and port, that combined make the address to bind the
listener to. These are bound in addition to the default address. - `envoy_gateway_bind_addresses` <code>(map<string|[address]>: nil)</code> - A
If `bridge` networking is in use, this map is automatically populated with additional map of additional addresses to be bound. The keys to this map are the same of
listeners enabling the Envoy proxy to work from inside the network namespace. the listeners to be created and the values are a map with two keys - address
and port, that combined make the address to bind the listener to. These are
bound in addition to the default address. If `bridge` networking is in use,
this map is automatically populated with additional listeners enabling the
Envoy proxy to work from inside the network namespace.
``` ```
envoy_gateway_bind_addresses "<service>" { envoy_gateway_bind_addresses "<service>" {
@@ -76,9 +81,9 @@ envoy_gateway_bind_addresses "<service>" {
service address from inside the network namespace. service address from inside the network namespace.
- `envoy_dns_discovery_type` `(string: optional)` - Determintes how Envoy will - `envoy_dns_discovery_type` `(string: optional)` - Determintes how Envoy will
resolve hostnames. Defaults to `LOGICAL_DNS`. Must be one of `STRICT_DNS` or resolve hostnames. Defaults to `LOGICAL_DNS`. Must be one of `STRICT_DNS` or
`LOGICAL_DNS`. Details for each type are available in the [Envoy Documentation](https://www.envoyproxy.io/docs/envoy/v1.16.1/intro/arch_overview/upstream/service_discovery). `LOGICAL_DNS`. Details for each type are available in the [Envoy
This option applies to terminating gateways that route to services addressed by a Documentation][envoy_sd_docs]. This option applies to terminating gateways
hostname. that route to services addressed by a hostname.
- `config` `(map: nil)` - Escape hatch for [Advanced Configuration] of Envoy. - `config` `(map: nil)` - Escape hatch for [Advanced Configuration] of Envoy.
Keys and values support [runtime variable interpolation][interpolation]. Keys and values support [runtime variable interpolation][interpolation].
@@ -89,51 +94,39 @@ envoy_gateway_bind_addresses "<service>" {
### `ingress` Parameters ### `ingress` Parameters
- `listener` <code>(array<[listener]> : required)</code> - One or more listeners
that the ingress gateway should setup, uniquely identified by their port
number.
- `tls` <code>([tls]: nil)</code> - TLS configuration for this gateway. - `tls` <code>([tls]: nil)</code> - TLS configuration for this gateway.
- `listener` <code>(array<[listener]> : required)</code> - One or more listeners that the
ingress gateway should setup, uniquely identified by their port number.
#### `tls` Parameters
- `enabled` `(bool: false)` - Set this configuration to enable TLS for every listener
on the gateway. If TLS is enabled, then each host defined in the `host` field will
be added as a DNSSAN to the gateway's x509 certificate.
- `tls_min_version` `(string: optional)` - Set the default minimum TLS version
supported by the gateway. Refer to
[`TLSMinVersion`](/consul/docs/connect/config-entries/ingress-gateway#tlsminversion)
in the Consul documentation for supported versions.
- `tls_max_version` `(string: optional)` - Set the default maximum TLS version
supported by the gateway. Refer to
[`TLSMaxVersion`](/consul/docs/connect/config-entries/ingress-gateway#tlsmaxversion)
in the Consul documentation for supported versions.
- `cipher_suites` `(array<string>: optional)` - Set the default list of TLS
cipher suites for the gateway's listeners. Refer to
[`CipherSuites`](/consul/docs/connect/config-entries/ingress-gateway#ciphersuites)
in the Consul documentation for the supported cipher suites.
#### `listener` Parameters #### `listener` Parameters
- `port` `(int: required)` - The port that the listener should receive traffic on. - `port` `(int: required)` - The port that the listener should receive traffic on.
- `protocol` `(string: "tcp")` - The protocol associated with the listener. One - `protocol` `(string: "tcp")` - The protocol associated with the listener. One
of `tcp`, `http`, `http2`, or `grpc`. of `tcp`, `http`, `http2`, or `grpc`.
~> **Note:** If using any protocol other than `tcp` (for example: `http` or `grpc`), preconfiguring a [service-default] in Consul to ~> **Note:** If using any protocol other than `tcp` (for example: `http` or
set the [Protocol](/consul/docs/connect/config-entries/service-defaults#protocol) `grpc`), preconfiguring a [service-default][] in Consul to set the
of the service to the desired protocol is mandatory due to an [open issue](https://github.com/hashicorp/nomad/issues/8647). [Protocol][service-default-protocol] of the service to the desired protocol is
mandatory due to an [open issue](https://github.com/hashicorp/nomad/issues/8647).
- `service` <code>(array<[service]>: required)</code> - One or more services to be - `service` <code>(array<[listener-service]>: required)</code> - One or more services to be
exposed via this listener. For `tcp` listeners, only a single service is allowed. exposed via this listener. For `tcp` listeners, only a single service is allowed.
#### `service` Parameters #### Listener `service` Parameters
The `service` blocks for a listener under an `ingress` gateway accept the
following parameters. Note these are different than the `service` blocks under a
`terminating` gateway.
- `name` `(string: required)` - The name of the service that should be exposed through - `name` `(string: required)` - The name of the service that should be exposed through
this listener. This can be either a service registered in the catalog, or a this listener. This can be either a service registered in the catalog, or a
service defined by other config entries, or a service that is going to be configured service defined by other config entries, or a service that is going to be configured
by Nomad. If the wildcard specifier `*` is provided, then ALL services will be by Nomad. If the wildcard specifier `*` is provided, then ALL services will be
exposed through this listener. This is not supported for a listener with protocol `tcp`. exposed through this listener. This is not supported for a listener with protocol `tcp`.
- `hosts` `(array<string>: nil)` - A list of hosts that specify what requests will - `hosts` `(array<string>: nil)` - A list of hosts that specify what requests will
match this service. This cannot be used with a `tcp` listener, and cannot be match this service. This cannot be used with a `tcp` listener, and cannot be
specified alongside a wildcard (`*`) service name. If not specified, the default specified alongside a wildcard (`*`) service name. If not specified, the default
@@ -148,6 +141,79 @@ envoy_gateway_bind_addresses "<service>" {
hosts are valid DNS records. For example, `*.example.com` is valid while `example.*` hosts are valid DNS records. For example, `*.example.com` is valid while `example.*`
and `*-suffix.example.com` are not. and `*-suffix.example.com` are not.
- `request_headers` `([header modifiers]: <optional>)` - A set of HTTP-specific
header modification rules that will be applied to requests routed to this
service. This cannot be used with a tcp listener.
- `response_headers` `([header modifiers]: <optional>)` - A set of HTTP-specific
header modification rules that will be applied to responses from this
service. This cannot be used with a tcp listener.
- `max_concurrent_requests` `(int: <optional>)` - Specifies the maximum number
of concurrent HTTP/2 traffic requests that are allowed at a single point in
time. If unset, will default to the Envoy proxy's default.
- `max_connections` `(int: <optional>)` - Specifies the maximum number of
HTTP/1.1 connections a service instance is allowed to establish against the
upstream. If unset, will default to the Envoy proxy's default.
- `max_pending_requests` `(int: <optional>)` - Specifies the maximum number of
requests that are allowed to queue while waiting to establish a connection.
If unset, will default to the Envoy proxy's default.
- `tls` <code>([tls]: nil)</code> - TLS configuration for this service.
#### Header modifier parameters
The `request_headers` and `response_headers` blocks of an `ingress.service`
block accept the following parameters. For more details, see the [Consul
documentation][response-headers].
- `add` `(map<string|string>: optional)` - A set of key-value pairs to add to the
headers, where header names are keys and header values are the values. Header
names are not case-sensitive. If header values with the same name already
exist, the value is appended and Consul applies both headers.
- `set` `(map<string|string>: optional)` - A set of key-value pairs to add to the
response header or to replace existing header values with. Use header names as
the keys. Header names are not case-sensitive. If header values with the same
names already exist, Consul replaces the header values.
- `remove` `array(string): optional` - Defines a list of headers to remove. Consul
removes only headers containing exact matches. Header names are not
case-sensitive.
#### `tls` Parameters
- `enabled` `(bool: false)` - Set this configuration to enable TLS for every
listener on the gateway. If TLS is enabled, then each host defined in the
`host` field will be added as a DNSSAN to the gateway's x509 certificate.
- `cipher_suites` `(array<string>: optional)` - Set the default list of TLS
cipher suites for the gateway's listeners. Refer to
[`CipherSuites`](/consul/docs/connect/config-entries/ingress-gateway#ciphersuites)
in the Consul documentation for the supported cipher suites.
- `sds` `(block: optional)` - Defines a set of parameters that configures the
listener to load TLS certificates from an external Secret Discovery Service
([SDS][]).
- `cluster_name` `(string)` - The SDS cluster name to connect to to retrieve
certificates.
- `cert_resource` `(string)` - The SDS resource name to request when fetching
the certificate from the SDS service.
- `tls_max_version` `(string: optional)` - Set the default maximum TLS version
supported by the gateway. Refer to
[`TLSMaxVersion`](/consul/docs/connect/config-entries/ingress-gateway#tlsmaxversion)
in the Consul documentation for supported versions.
- `tls_min_version` `(string: optional)` - Set the default minimum TLS version
supported by the gateway. Refer to
[`TLSMinVersion`](/consul/docs/connect/config-entries/ingress-gateway#tlsminversion)
in the Consul documentation for supported versions.
### `terminating` Parameters ### `terminating` Parameters
- `service` <code>(array<[linked-service]>: required)</code> - One or more services to be - `service` <code>(array<[linked-service]>: required)</code> - One or more services to be
@@ -156,26 +222,35 @@ envoy_gateway_bind_addresses "<service>" {
addresses. They must also be registered in the same Consul datacenter as the addresses. They must also be registered in the same Consul datacenter as the
terminating gateway. terminating gateway.
#### `service` Parameters #### linked `service` Parameters
- `name` `(string: required)` - The name of the service to link with the gateway. The `service` blocks for a `terminating` gateway accept the following
If the wildcard specifier `*` is provided, then ALL services within the Consul parameters. Note these are different than the `service` blocks for listeners
namespace wil lbe linked with the gateway. under an `ingress` gateway.
- `name` `(string: required)` - The name of the service to link with the
gateway. If the wildcard specifier `*` is provided, then ALL services within
the Consul namespace wil lbe linked with the gateway.
- `ca_file` `(string: <optional>)` - A file path to a PEM-encoded certificate - `ca_file` `(string: <optional>)` - A file path to a PEM-encoded certificate
authority. The file must be accessible by the gateway task. The certificate authority authority. The file must be accessible by the gateway task. The certificate
is used to verify the authenticity of the service linked with the gateway. It authority is used to verify the authenticity of the service linked with the
can be provided along with a `cert_file` and `key_file` for mutual TLS gateway. It can be provided along with a `cert_file` and `key_file` for mutual
authentication, or on its own for one-way TLS authentication. If none is provided TLS authentication, or on its own for one-way TLS authentication. If none is
the gateway **will not** encrypt traffic to the destination. provided the gateway **will not** encrypt traffic to the destination.
- `cert_file` `(string: <optional>)` - A file path to a PEM-encoded certificate. - `cert_file` `(string: <optional>)` - A file path to a PEM-encoded certificate.
The file must be accessible by the gateway task. The certificate is provided to servers The file must be accessible by the gateway task. The certificate is provided
to verify the gateway's authenticity. It must be provided if a `key_file` is provided. to servers to verify the gateway's authenticity. It must be provided if a
`key_file` is provided.
- `key_file` `(string: <optional>)` - A file path to a PEM-encoded private key. - `key_file` `(string: <optional>)` - A file path to a PEM-encoded private key.
The file must be accessible by the gateway task. The key is used with the certificate The file must be accessible by the gateway task. The key is used with the
to verify the gateway's authenticity. It must be provided if a `cert_file` is provided. certificate to verify the gateway's authenticity. It must be provided if a
- `sni` `(string: <optional>)` - An optional hostname or domain name to specify during `cert_file` is provided.
the TLS handshake.
- `sni` `(string: <optional>)` - An optional hostname or domain name to specify
during the TLS handshake.
### `mesh` Parameters ### `mesh` Parameters
@@ -654,13 +729,18 @@ job "countdash-mesh-two" {
[envoy docker]: https://hub.docker.com/r/envoyproxy/envoy/tags [envoy docker]: https://hub.docker.com/r/envoyproxy/envoy/tags
[ingress]: /nomad/docs/job-specification/gateway#ingress-parameters [ingress]: /nomad/docs/job-specification/gateway#ingress-parameters
[proxy]: /nomad/docs/job-specification/gateway#proxy-parameters [proxy]: /nomad/docs/job-specification/gateway#proxy-parameters
[linked-service]: /nomad/docs/job-specification/gateway#service-parameters-1 [linked-service]: /nomad/docs/job-specification/gateway#linked-service-parameters
[listener]: /nomad/docs/job-specification/gateway#listener-parameters [listener]: /nomad/docs/job-specification/gateway#listener-parameters
[interpolation]: /nomad/docs/runtime/interpolation [interpolation]: /nomad/docs/runtime/interpolation
[service]: /nomad/docs/job-specification/gateway#service-parameters [listener-service]: /nomad/docs/job-specification/gateway#listener-service-parameters
[service-default]: /consul/docs/connect/config-entries/service-defaults [service-default]: /consul/docs/connect/config-entries/service-defaults
[sidecar_task]: /nomad/docs/job-specification/sidecar_task [sidecar_task]: /nomad/docs/job-specification/sidecar_task
[terminating]: /nomad/docs/job-specification/gateway#terminating-parameters [terminating]: /nomad/docs/job-specification/gateway#terminating-parameters
[tls]: /nomad/docs/job-specification/gateway#tls-parameters [tls]: /nomad/docs/job-specification/gateway#tls-parameters
[mesh]: /nomad/docs/job-specification/gateway#mesh-parameters [mesh]: /nomad/docs/job-specification/gateway#mesh-parameters
[meta]: /nomad/docs/job-specification/service#meta [meta]: /nomad/docs/job-specification/service#meta
[envoy_sd_docs]: https://www.envoyproxy.io/docs/envoy/v1.16.1/intro/arch_overview/upstream/service_discovery
[SDS]: https://developer.hashicorp.com/consul/docs/connect/config-entries/ingress-gateway#listeners-services-tls-sds
[service-default-protocol]: /consul/docs/connect/config-entries/service-defaults#protocol
[response-headers]: /consul/docs/connect/config-entries/ingress-gateway#listeners-services-responseheaders
[header modifiers]: /nomad/docs/job-specification/gateway#header-modifier-parameters