mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
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:
3
.changelog/16753.txt
Normal file
3
.changelog/16753.txt
Normal 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
|
||||
```
|
||||
161
api/consul.go
161
api/consul.go
@@ -4,6 +4,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"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 {
|
||||
Enabled bool `hcl:"enabled,optional"`
|
||||
TLSMinVersion string `hcl:"tls_min_version,optional" mapstructure:"tls_min_version"`
|
||||
TLSMaxVersion string `hcl:"tls_max_version,optional" mapstructure:"tls_max_version"`
|
||||
CipherSuites []string `hcl:"cipher_suites,optional" mapstructure:"cipher_suites"`
|
||||
|
||||
// Enabled indicates whether TLS is enabled for the configuration entry
|
||||
Enabled bool `hcl:"enabled,optional"`
|
||||
|
||||
// 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() {
|
||||
@@ -420,6 +461,7 @@ func (tc *ConsulGatewayTLSConfig) Copy() *ConsulGatewayTLSConfig {
|
||||
Enabled: tc.Enabled,
|
||||
TLSMinVersion: tc.TLSMinVersion,
|
||||
TLSMaxVersion: tc.TLSMaxVersion,
|
||||
SDS: tc.SDS.Copy(),
|
||||
}
|
||||
if len(tc.CipherSuites) != 0 {
|
||||
cipherSuites := make([]string, len(tc.CipherSuites))
|
||||
@@ -430,13 +472,90 @@ func (tc *ConsulGatewayTLSConfig) Copy() *ConsulGatewayTLSConfig {
|
||||
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 {
|
||||
// Namespace is not yet supported.
|
||||
// Namespace string
|
||||
|
||||
// Name of the service exposed through this listener.
|
||||
Name string `hcl:"name,optional"`
|
||||
|
||||
// Hosts specifies one or more hosts that the listening services can receive
|
||||
// requests on.
|
||||
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() {
|
||||
@@ -447,6 +566,9 @@ func (s *ConsulIngressService) Canonicalize() {
|
||||
if len(s.Hosts) == 0 {
|
||||
s.Hosts = nil
|
||||
}
|
||||
|
||||
s.RequestHeaders.Canonicalize()
|
||||
s.ResponseHeaders.Canonicalize()
|
||||
}
|
||||
|
||||
func (s *ConsulIngressService) Copy() *ConsulIngressService {
|
||||
@@ -454,16 +576,19 @@ func (s *ConsulIngressService) Copy() *ConsulIngressService {
|
||||
return nil
|
||||
}
|
||||
|
||||
var hosts []string = nil
|
||||
if n := len(s.Hosts); n > 0 {
|
||||
hosts = make([]string, n)
|
||||
copy(hosts, s.Hosts)
|
||||
}
|
||||
ns := new(ConsulIngressService)
|
||||
*ns = *s
|
||||
|
||||
return &ConsulIngressService{
|
||||
Name: s.Name,
|
||||
Hosts: hosts,
|
||||
}
|
||||
ns.Hosts = slices.Clone(s.Hosts)
|
||||
ns.RequestHeaders = s.RequestHeaders.Copy()
|
||||
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 (
|
||||
@@ -521,7 +646,11 @@ type ConsulIngressConfigEntry struct {
|
||||
// Namespace is not yet supported.
|
||||
// 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"`
|
||||
}
|
||||
|
||||
|
||||
@@ -415,6 +415,33 @@ func TestConsulIngressConfigEntry_Copy(t *testing.T) {
|
||||
Services: []*ConsulIngressService{{
|
||||
Name: "service1",
|
||||
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",
|
||||
Hosts: []string{"2.2.2.2"},
|
||||
|
||||
@@ -33,3 +33,12 @@ func formatFloat(f float64, maxPrec int) string {
|
||||
func pointerOf[A any](a A) *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
|
||||
}
|
||||
|
||||
@@ -1745,6 +1745,18 @@ func apiConnectGatewayTLSConfig(in *api.ConsulGatewayTLSConfig) *structs.ConsulG
|
||||
TLSMinVersion: in.TLSMinVersion,
|
||||
TLSMaxVersion: in.TLSMaxVersion,
|
||||
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{
|
||||
Name: in.Name,
|
||||
Hosts: slices.Clone(in.Hosts),
|
||||
Name: in.Name,
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4182,6 +4182,33 @@ func TestConversion_ApiConsulConnectToStructs(t *testing.T) {
|
||||
Services: []*structs.ConsulIngressService{{
|
||||
Name: "ingress1",
|
||||
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{{
|
||||
Name: "ingress1",
|
||||
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)),
|
||||
}},
|
||||
}},
|
||||
},
|
||||
|
||||
@@ -425,10 +425,84 @@ func parseGatewayProxy(o *ast.ObjectItem) (*api.ConsulGatewayProxy, error) {
|
||||
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) {
|
||||
valid := []string{
|
||||
"name",
|
||||
"hosts",
|
||||
"tls",
|
||||
"request_headers",
|
||||
"response_headers",
|
||||
"max_connections",
|
||||
"max_pending_requests",
|
||||
"max_concurrent_requests",
|
||||
}
|
||||
|
||||
if err := checkHCLKeys(o.Val, valid); err != nil {
|
||||
@@ -441,6 +515,10 @@ func parseConsulIngressService(o *ast.ObjectItem) (*api.ConsulIngressService, er
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delete(m, "tls")
|
||||
delete(m, "request_headers")
|
||||
delete(m, "response_headers")
|
||||
|
||||
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
Result: &service,
|
||||
})
|
||||
@@ -452,6 +530,37 @@ func parseConsulIngressService(o *ast.ObjectItem) (*api.ConsulIngressService, er
|
||||
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
|
||||
}
|
||||
|
||||
@@ -541,12 +650,43 @@ func parseConsulIngressListener(o *ast.ObjectItem) (*api.ConsulIngressListener,
|
||||
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) {
|
||||
valid := []string{
|
||||
"enabled",
|
||||
"tls_min_version",
|
||||
"tls_max_version",
|
||||
"cipher_suites",
|
||||
"sds",
|
||||
}
|
||||
|
||||
if err := checkHCLKeys(o.Val, valid); err != nil {
|
||||
@@ -559,6 +699,8 @@ func parseConsulGatewayTLS(o *ast.ObjectItem) (*api.ConsulGatewayTLSConfig, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delete(m, "sds")
|
||||
|
||||
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
Result: &tls,
|
||||
})
|
||||
@@ -570,6 +712,22 @@ func parseConsulGatewayTLS(o *ast.ObjectItem) (*api.ConsulGatewayTLSConfig, erro
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
capi "github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/nomad/api"
|
||||
"github.com/hashicorp/nomad/ci"
|
||||
"github.com/hashicorp/nomad/helper/pointer"
|
||||
"github.com/shoenig/test/must"
|
||||
)
|
||||
|
||||
@@ -1682,6 +1683,23 @@ func TestParse(t *testing.T) {
|
||||
Hosts: []string{
|
||||
"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)),
|
||||
}},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -50,6 +50,23 @@ job "connect_gateway_ingress" {
|
||||
service {
|
||||
name = "nginx"
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -587,9 +588,21 @@ func convertIngressCE(namespace, service string, entry *structs.ConsulIngressCon
|
||||
for _, listener := range entry.Listeners {
|
||||
var services []api.IngressService = nil
|
||||
for _, s := range listener.Services {
|
||||
var sds *api.GatewayTLSSDSConfig = nil
|
||||
if s.TLS != nil {
|
||||
sds = convertGatewayTLSSDSConfig(s.TLS.SDS)
|
||||
}
|
||||
services = append(services, api.IngressService{
|
||||
Name: s.Name,
|
||||
Hosts: slices.Clone(s.Hosts),
|
||||
Name: s.Name,
|
||||
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{
|
||||
@@ -611,11 +624,48 @@ func convertIngressCE(namespace, service string, entry *structs.ConsulIngressCon
|
||||
Namespace: namespace,
|
||||
Kind: api.IngressGateway,
|
||||
Name: service,
|
||||
TLS: tls,
|
||||
TLS: *convertGatewayTLSConfig(entry.TLS),
|
||||
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 {
|
||||
var linked []api.LinkedService = nil
|
||||
for _, s := range entry.Services {
|
||||
|
||||
@@ -1314,6 +1314,11 @@ func connectGatewayTLSConfigDiff(prev, next *ConsulGatewayTLSConfig, contextual
|
||||
// Diff the primitive field.
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1443,6 +1448,38 @@ func connectGatewayIngressServiceDiff(prev, next *ConsulIngressService, contextu
|
||||
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.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual)
|
||||
|
||||
@@ -1451,6 +1488,55 @@ func connectGatewayIngressServiceDiff(prev, next *ConsulIngressService, contextu
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -1994,12 +1994,61 @@ func (p *ConsulGatewayProxy) Validate() error {
|
||||
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 {
|
||||
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
|
||||
|
||||
// TLSMaxVersion specifies the maxmimum TLS version supported for gateway
|
||||
// listeners.
|
||||
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 {
|
||||
@@ -2012,6 +2061,7 @@ func (c *ConsulGatewayTLSConfig) Copy() *ConsulGatewayTLSConfig {
|
||||
TLSMinVersion: c.TLSMinVersion,
|
||||
TLSMaxVersion: c.TLSMaxVersion,
|
||||
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 &&
|
||||
c.TLSMinVersion == o.TLSMinVersion &&
|
||||
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.
|
||||
// For more details, consult the Consul documentation:
|
||||
// https://developer.hashicorp.com/consul/docs/connect/config-entries/ingress-gateway
|
||||
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
|
||||
|
||||
// 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 {
|
||||
@@ -2037,16 +2169,19 @@ func (s *ConsulIngressService) Copy() *ConsulIngressService {
|
||||
return nil
|
||||
}
|
||||
|
||||
var hosts []string = nil
|
||||
if n := len(s.Hosts); n > 0 {
|
||||
hosts = make([]string, n)
|
||||
copy(hosts, s.Hosts)
|
||||
}
|
||||
ns := new(ConsulIngressService)
|
||||
*ns = *s
|
||||
|
||||
return &ConsulIngressService{
|
||||
Name: s.Name,
|
||||
Hosts: hosts,
|
||||
}
|
||||
ns.Hosts = slices.Clone(s.Hosts)
|
||||
ns.RequestHeaders = s.RequestHeaders.Copy()
|
||||
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 {
|
||||
@@ -2058,7 +2193,35 @@ func (s *ConsulIngressService) Equal(o *ConsulIngressService) bool {
|
||||
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 {
|
||||
@@ -2171,7 +2334,12 @@ func ingressServicesEqual(a, b []*ConsulIngressService) bool {
|
||||
//
|
||||
// https://www.consul.io/docs/agent/config-entries/ingress-gateway#available-fields
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,10 @@ description: |-
|
||||
|
||||
<Placement groups={['job', 'group', 'service', 'connect', 'gateway']} />
|
||||
|
||||
The `gateway` block allows configuration of [Consul Connect Gateways](/consul/docs/connect/gateways). Nomad will
|
||||
automatically create the 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` block allows configuration of [Consul Connect
|
||||
Gateways](/consul/docs/connect/gateways). Nomad will automatically create the
|
||||
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.
|
||||
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
|
||||
|
||||
- `connect_timeout` `(string: "5s")` - The amount of time to allow when making upstream
|
||||
connections before timing out. Defaults to 5 seconds. If the upstream service has
|
||||
the configuration option <code>[connect_timeout_ms]</code> set for the `service-resolver`, that
|
||||
timeout value will take precedence over this gateway proxy option.
|
||||
- `envoy_gateway_bind_tagged_addresses` `(bool: false)` - Indicates that the gateway
|
||||
services tagged addresses should be bound to listeners in addition to the default
|
||||
listener address.
|
||||
- `envoy_gateway_bind_addresses` <code>(map<string|[address]>: nil)</code> - A map of additional addresses to be bound.
|
||||
The keys to this map are the same of 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.
|
||||
- `connect_timeout` `(string: "5s")` - The amount of time to allow when making
|
||||
upstream connections before timing out. Defaults to 5 seconds. If the upstream
|
||||
service has the configuration option <code>[connect_timeout_ms]</code> set for
|
||||
the `service-resolver`, that timeout value will take precedence over this
|
||||
gateway proxy option.
|
||||
|
||||
- `envoy_gateway_bind_tagged_addresses` `(bool: false)` - Indicates that the
|
||||
gateway services tagged addresses should be bound to listeners in addition to
|
||||
the default listener address.
|
||||
|
||||
- `envoy_gateway_bind_addresses` <code>(map<string|[address]>: nil)</code> - A
|
||||
map of additional addresses to be bound. The keys to this map are the same of
|
||||
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>" {
|
||||
@@ -76,9 +81,9 @@ envoy_gateway_bind_addresses "<service>" {
|
||||
service address from inside the network namespace.
|
||||
- `envoy_dns_discovery_type` `(string: optional)` - Determintes how Envoy will
|
||||
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).
|
||||
This option applies to terminating gateways that route to services addressed by a
|
||||
hostname.
|
||||
`LOGICAL_DNS`. Details for each type are available in the [Envoy
|
||||
Documentation][envoy_sd_docs]. This option applies to terminating gateways
|
||||
that route to services addressed by a hostname.
|
||||
- `config` `(map: nil)` - Escape hatch for [Advanced Configuration] of Envoy.
|
||||
Keys and values support [runtime variable interpolation][interpolation].
|
||||
|
||||
@@ -89,51 +94,39 @@ envoy_gateway_bind_addresses "<service>" {
|
||||
|
||||
### `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.
|
||||
- `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
|
||||
|
||||
- `port` `(int: required)` - The port that the listener should receive traffic on.
|
||||
|
||||
- `protocol` `(string: "tcp")` - The protocol associated with the listener. One
|
||||
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
|
||||
set the [Protocol](/consul/docs/connect/config-entries/service-defaults#protocol)
|
||||
of the service to the desired protocol is mandatory due to an [open issue](https://github.com/hashicorp/nomad/issues/8647).
|
||||
~> **Note:** If using any protocol other than `tcp` (for example: `http` or
|
||||
`grpc`), preconfiguring a [service-default][] in Consul to set the
|
||||
[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.
|
||||
|
||||
#### `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
|
||||
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
|
||||
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`.
|
||||
|
||||
- `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
|
||||
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.*`
|
||||
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
|
||||
|
||||
- `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
|
||||
terminating gateway.
|
||||
|
||||
#### `service` Parameters
|
||||
#### linked `service` Parameters
|
||||
|
||||
- `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.
|
||||
The `service` blocks for a `terminating` gateway accept the following
|
||||
parameters. Note these are different than the `service` blocks for listeners
|
||||
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
|
||||
authority. The file must be accessible by the gateway task. The certificate authority
|
||||
is used to verify the authenticity of the service linked with the gateway. It
|
||||
can be provided along with a `cert_file` and `key_file` for mutual TLS
|
||||
authentication, or on its own for one-way TLS authentication. If none is provided
|
||||
the gateway **will not** encrypt traffic to the destination.
|
||||
authority. The file must be accessible by the gateway task. The certificate
|
||||
authority is used to verify the authenticity of the service linked with the
|
||||
gateway. It can be provided along with a `cert_file` and `key_file` for mutual
|
||||
TLS authentication, or on its own for one-way TLS authentication. If none is
|
||||
provided the gateway **will not** encrypt traffic to the destination.
|
||||
|
||||
- `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
|
||||
to verify the gateway's authenticity. It must be provided if a `key_file` is provided.
|
||||
The file must be accessible by the gateway task. The certificate 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.
|
||||
The file must be accessible by the gateway task. The key is used with the certificate
|
||||
to verify the gateway's authenticity. It must be provided if a `cert_file` is provided.
|
||||
- `sni` `(string: <optional>)` - An optional hostname or domain name to specify during
|
||||
the TLS handshake.
|
||||
The file must be accessible by the gateway task. The key is used with the
|
||||
certificate to verify the gateway's authenticity. It must be provided if a
|
||||
`cert_file` is provided.
|
||||
|
||||
- `sni` `(string: <optional>)` - An optional hostname or domain name to specify
|
||||
during the TLS handshake.
|
||||
|
||||
### `mesh` Parameters
|
||||
|
||||
@@ -654,13 +729,18 @@ job "countdash-mesh-two" {
|
||||
[envoy docker]: https://hub.docker.com/r/envoyproxy/envoy/tags
|
||||
[ingress]: /nomad/docs/job-specification/gateway#ingress-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
|
||||
[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
|
||||
[sidecar_task]: /nomad/docs/job-specification/sidecar_task
|
||||
[terminating]: /nomad/docs/job-specification/gateway#terminating-parameters
|
||||
[tls]: /nomad/docs/job-specification/gateway#tls-parameters
|
||||
[mesh]: /nomad/docs/job-specification/gateway#mesh-parameters
|
||||
[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
|
||||
|
||||
Reference in New Issue
Block a user