diff --git a/.changelog/13184.txt b/.changelog/13184.txt new file mode 100644 index 000000000..ab657c230 --- /dev/null +++ b/.changelog/13184.txt @@ -0,0 +1,3 @@ +```release-note:improvements +consul/connect: Allow configuring tls settings for ingress. +``` diff --git a/api/consul.go b/api/consul.go index 93959ba26..db25a1664 100644 --- a/api/consul.go +++ b/api/consul.go @@ -358,7 +358,10 @@ func (p *ConsulGatewayProxy) Copy() *ConsulGatewayProxy { // ConsulGatewayTLSConfig is used to configure TLS for a gateway. type ConsulGatewayTLSConfig struct { - Enabled bool `hcl:"enabled,optional"` + 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"` } func (tc *ConsulGatewayTLSConfig) Canonicalize() { @@ -369,9 +372,18 @@ func (tc *ConsulGatewayTLSConfig) Copy() *ConsulGatewayTLSConfig { return nil } - return &ConsulGatewayTLSConfig{ - Enabled: tc.Enabled, + result := &ConsulGatewayTLSConfig{ + Enabled: tc.Enabled, + TLSMinVersion: tc.TLSMinVersion, + TLSMaxVersion: tc.TLSMaxVersion, } + if len(tc.CipherSuites) != 0 { + cipherSuites := make([]string, len(tc.CipherSuites)) + copy(cipherSuites, tc.CipherSuites) + result.CipherSuites = cipherSuites + } + + return result } // ConsulIngressService is used to configure a service fronted by the ingress gateway. diff --git a/api/consul_test.go b/api/consul_test.go index 0a4c6781c..2556ab662 100644 --- a/api/consul_test.go +++ b/api/consul_test.go @@ -516,3 +516,32 @@ func TestConsulMeshGateway_Copy(t *testing.T) { require.Equal(t, c, result) }) } + +func TestConsulGatewayTLSConfig_Copy(t *testing.T) { + testutil.Parallel(t) + + t.Run("nil", func(t *testing.T) { + c := (*ConsulGatewayTLSConfig)(nil) + result := c.Copy() + require.Nil(t, result) + }) + + t.Run("enabled", func(t *testing.T) { + c := &ConsulGatewayTLSConfig{ + Enabled: true, + } + result := c.Copy() + require.Equal(t, c, result) + }) + + t.Run("customized", func(t *testing.T) { + c := &ConsulGatewayTLSConfig{ + Enabled: true, + TLSMinVersion: "TLSv1_2", + TLSMaxVersion: "TLSv1_3", + CipherSuites: []string{"foo", "bar"}, + } + result := c.Copy() + require.Equal(t, c, result) + }) +} diff --git a/command/agent/job_endpoint.go b/command/agent/job_endpoint.go index 3cbd1f3c7..f88a58a2e 100644 --- a/command/agent/job_endpoint.go +++ b/command/agent/job_endpoint.go @@ -1503,7 +1503,10 @@ func apiConnectGatewayTLSConfig(in *api.ConsulGatewayTLSConfig) *structs.ConsulG } return &structs.ConsulGatewayTLSConfig{ - Enabled: in.Enabled, + Enabled: in.Enabled, + TLSMinVersion: in.TLSMinVersion, + TLSMaxVersion: in.TLSMaxVersion, + CipherSuites: helper.CopySliceString(in.CipherSuites), } } diff --git a/command/agent/job_endpoint_test.go b/command/agent/job_endpoint_test.go index 1741f13b1..28e08ed31 100644 --- a/command/agent/job_endpoint_test.go +++ b/command/agent/job_endpoint_test.go @@ -3817,7 +3817,12 @@ func TestConversion_ApiConsulConnectToStructs(t *testing.T) { require.Equal(t, &structs.ConsulConnect{ Gateway: &structs.ConsulGateway{ Ingress: &structs.ConsulIngressConfigEntry{ - TLS: &structs.ConsulGatewayTLSConfig{Enabled: true}, + TLS: &structs.ConsulGatewayTLSConfig{ + Enabled: true, + TLSMinVersion: "TLSv1_2", + TLSMaxVersion: "TLSv1_3", + CipherSuites: []string{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"}, + }, Listeners: []*structs.ConsulIngressListener{{ Port: 1111, Protocol: "http", @@ -3832,7 +3837,12 @@ func TestConversion_ApiConsulConnectToStructs(t *testing.T) { &api.ConsulConnect{ Gateway: &api.ConsulGateway{ Ingress: &api.ConsulIngressConfigEntry{ - TLS: &api.ConsulGatewayTLSConfig{Enabled: true}, + TLS: &api.ConsulGatewayTLSConfig{ + Enabled: true, + TLSMinVersion: "TLSv1_2", + TLSMaxVersion: "TLSv1_3", + CipherSuites: []string{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"}, + }, Listeners: []*api.ConsulIngressListener{{ Port: 1111, Protocol: "http", diff --git a/go.mod b/go.mod index 1600451c3..e9a8d2408 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.2.1-0.20200228141219-3ce3d519df39 github.com/hashicorp/consul v1.7.8 github.com/hashicorp/consul-template v0.29.0 - github.com/hashicorp/consul/api v1.12.0 + github.com/hashicorp/consul/api v1.13.0 github.com/hashicorp/consul/sdk v0.8.0 github.com/hashicorp/cronexpr v1.1.1 github.com/hashicorp/go-bexpr v0.1.11 diff --git a/go.sum b/go.sum index f48b82b59..24118b96f 100644 --- a/go.sum +++ b/go.sum @@ -583,6 +583,7 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0= @@ -657,8 +658,8 @@ github.com/hashicorp/consul v1.7.8/go.mod h1:urbfGaVZDmnXC6geg0LYPh/SRUk1E8nfmDH github.com/hashicorp/consul-template v0.29.0 h1:rDmF3Wjqp5ztCq054MruzEpi9ArcyJ/Rp4eWrDhMldM= github.com/hashicorp/consul-template v0.29.0/go.mod h1:p1A8Z6Mz7gbXu38SI1c9nt5ItBK7ACWZG4ZE1A5Tr2M= github.com/hashicorp/consul/api v1.4.0/go.mod h1:xc8u05kyMa3Wjr9eEAsIAo3dg8+LywT5E/Cl7cNS5nU= -github.com/hashicorp/consul/api v1.12.0 h1:k3y1FYv6nuKyNTqj6w9gXOx5r5CfLj/k/euUeBXj1OY= -github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= +github.com/hashicorp/consul/api v1.13.0 h1:2hnLQ0GjQvw7f3O61jMO8gbasZviZTrt9R8WzgiirHc= +github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= github.com/hashicorp/consul/sdk v0.4.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= @@ -745,8 +746,6 @@ github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 h1:SMGUnbpAcat8rIKHkBPjf github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1/go.mod h1:Ch/bf00Qnx77MZd49JRgHYqHQjtEmTgGU2faufpVZb0= github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1 h1:Yc026VyMyIpq1UWRnakHRG01U8fJm+nEfEmjoAb00n8= github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs= diff --git a/jobspec/parse_service.go b/jobspec/parse_service.go index 278942fef..7dc10214b 100644 --- a/jobspec/parse_service.go +++ b/jobspec/parse_service.go @@ -541,6 +541,9 @@ func parseConsulIngressListener(o *ast.ObjectItem) (*api.ConsulIngressListener, func parseConsulGatewayTLS(o *ast.ObjectItem) (*api.ConsulGatewayTLSConfig, error) { valid := []string{ "enabled", + "tls_min_version", + "tls_max_version", + "cipher_suites", } if err := checkHCLKeys(o.Val, valid); err != nil { diff --git a/jobspec/parse_test.go b/jobspec/parse_test.go index 861e73a07..7c9ff243a 100644 --- a/jobspec/parse_test.go +++ b/jobspec/parse_test.go @@ -1560,7 +1560,9 @@ func TestParse(t *testing.T) { }, Ingress: &api.ConsulIngressConfigEntry{ TLS: &api.ConsulGatewayTLSConfig{ - Enabled: true, + Enabled: true, + TLSMinVersion: "TLSv1_2", + CipherSuites: []string{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"}, }, Listeners: []*api.ConsulIngressListener{{ Port: 8001, diff --git a/jobspec/test-fixtures/tg-service-connect-gateway-ingress.hcl b/jobspec/test-fixtures/tg-service-connect-gateway-ingress.hcl index 453440df7..2474a0fcd 100644 --- a/jobspec/test-fixtures/tg-service-connect-gateway-ingress.hcl +++ b/jobspec/test-fixtures/tg-service-connect-gateway-ingress.hcl @@ -23,7 +23,9 @@ job "connect_gateway_ingress" { } ingress { tls { - enabled = true + enabled = true + tls_min_version = "TLSv1_2" + cipher_suites = ["TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"] } listener { diff --git a/nomad/consul.go b/nomad/consul.go index 61a04816f..7eac19ff8 100644 --- a/nomad/consul.go +++ b/nomad/consul.go @@ -603,16 +603,19 @@ func convertIngressCE(namespace, service string, entry *structs.ConsulIngressCon }) } - tlsEnabled := false - if entry.TLS != nil && entry.TLS.Enabled { - tlsEnabled = true + tls := api.GatewayTLSConfig{} + if entry.TLS != nil { + tls.Enabled = entry.TLS.Enabled + tls.TLSMinVersion = entry.TLS.TLSMinVersion + tls.TLSMaxVersion = entry.TLS.TLSMaxVersion + tls.CipherSuites = helper.CopySliceString(entry.TLS.CipherSuites) } return &api.IngressGatewayConfigEntry{ Namespace: namespace, Kind: api.IngressGateway, Name: service, - TLS: api.GatewayTLSConfig{Enabled: tlsEnabled}, + TLS: tls, Listeners: listeners, } } diff --git a/nomad/job_endpoint_hook_connect.go b/nomad/job_endpoint_hook_connect.go index b2dc23a5c..0cba5c895 100644 --- a/nomad/job_endpoint_hook_connect.go +++ b/nomad/job_endpoint_hook_connect.go @@ -89,6 +89,17 @@ func connectGatewayVersionConstraint() *structs.Constraint { } } +// connectGatewayTLSVersionConstraint is used when building a connect gateway +// task to ensure proper Consul version is used that supports customized TLS version. +// https://github.com/hashicorp/consul/pull/11576 +func connectGatewayTLSVersionConstraint() *structs.Constraint { + return &structs.Constraint{ + LTarget: "${attr.consul.version}", + RTarget: ">= 1.11.2", + Operand: structs.ConstraintSemver, + } +} + func connectListenerConstraint() *structs.Constraint { return &structs.Constraint{ LTarget: "${attr.consul.grpc}", @@ -315,7 +326,9 @@ func groupConnectHook(job *structs.Job, g *structs.TaskGroup) error { // detect whether the group is in host networking mode, which will // require tweaking the default gateway task config netHost := netMode == "host" - task := newConnectGatewayTask(prefix, service.Name, netHost) + customizedTLS := service.Connect.IsCustomizedTLS() + + task := newConnectGatewayTask(prefix, service.Name, netHost, customizedTLS) g.Tasks = append(g.Tasks, task) // the connect.sidecar_task stanza can also be used to configure @@ -434,7 +447,14 @@ func gatewayBindAddressesIngressForBridge(ingress *structs.ConsulIngressConfigEn return addresses } -func newConnectGatewayTask(prefix, service string, netHost bool) *structs.Task { +func newConnectGatewayTask(prefix, service string, netHost, customizedTls bool) *structs.Task { + constraints := structs.Constraints{ + connectGatewayVersionConstraint(), + connectListenerConstraint(), + } + if customizedTls { + constraints = append(constraints, connectGatewayTLSVersionConstraint()) + } return &structs.Task{ // Name is used in container name so must start with '[A-Za-z0-9]' Name: fmt.Sprintf("%s-%s", prefix, service), @@ -446,11 +466,8 @@ func newConnectGatewayTask(prefix, service string, netHost bool) *structs.Task { MaxFiles: 2, MaxFileSizeMB: 2, }, - Resources: connectSidecarResources(), - Constraints: structs.Constraints{ - connectGatewayVersionConstraint(), - connectListenerConstraint(), - }, + Resources: connectSidecarResources(), + Constraints: constraints, } } diff --git a/nomad/job_endpoint_hook_connect_test.go b/nomad/job_endpoint_hook_connect_test.go index ed6b28e95..b665205ca 100644 --- a/nomad/job_endpoint_hook_connect_test.go +++ b/nomad/job_endpoint_hook_connect_test.go @@ -124,12 +124,16 @@ func TestJobEndpointConnect_groupConnectHook_IngressGateway_BridgeNetwork(t *tes "gateway_name": "my-gateway", } job.TaskGroups[0].Services[0].Name = "${NOMAD_META_gateway_name}" + job.TaskGroups[0].Services[0].Connect.Gateway.Ingress.TLS = &structs.ConsulGatewayTLSConfig{ + Enabled: true, + TLSMinVersion: "TLSv1_2", + } // setup expectations expTG := job.TaskGroups[0].Copy() expTG.Tasks = []*structs.Task{ // inject the gateway task - newConnectGatewayTask(structs.ConnectIngressPrefix, "my-gateway", false), + newConnectGatewayTask(structs.ConnectIngressPrefix, "my-gateway", false, true), } expTG.Services[0].Name = "my-gateway" expTG.Tasks[0].Canonicalize(job, expTG) @@ -144,6 +148,13 @@ func TestJobEndpointConnect_groupConnectHook_IngressGateway_BridgeNetwork(t *tes // Test that the hook is idempotent require.NoError(t, groupConnectHook(job, job.TaskGroups[0])) require.Exactly(t, expTG, job.TaskGroups[0]) + + // Test that the hook populates the correct constraint for customized tls + require.Contains(t, job.TaskGroups[0].Tasks[0].Constraints, &structs.Constraint{ + LTarget: "${attr.consul.version}", + RTarget: ">= 1.11.2", + Operand: structs.ConstraintSemver, + }) } func TestJobEndpointConnect_groupConnectHook_IngressGateway_HostNetwork(t *testing.T) { @@ -161,7 +172,7 @@ func TestJobEndpointConnect_groupConnectHook_IngressGateway_HostNetwork(t *testi expTG := job.TaskGroups[0].Copy() expTG.Tasks = []*structs.Task{ // inject the gateway task - newConnectGatewayTask(structs.ConnectIngressPrefix, "my-gateway", true), + newConnectGatewayTask(structs.ConnectIngressPrefix, "my-gateway", true, false), } expTG.Services[0].Name = "my-gateway" expTG.Tasks[0].Canonicalize(job, expTG) @@ -263,7 +274,7 @@ func TestJobEndpointConnect_groupConnectHook_TerminatingGateway(t *testing.T) { expTG := job.TaskGroups[0].Copy() expTG.Tasks = []*structs.Task{ // inject the gateway task - newConnectGatewayTask(structs.ConnectTerminatingPrefix, "my-gateway", false), + newConnectGatewayTask(structs.ConnectTerminatingPrefix, "my-gateway", false, false), } expTG.Services[0].Name = "my-gateway" expTG.Tasks[0].Canonicalize(job, expTG) @@ -297,7 +308,7 @@ func TestJobEndpointConnect_groupConnectHook_MeshGateway(t *testing.T) { expTG := job.TaskGroups[0].Copy() expTG.Tasks = []*structs.Task{ // inject the gateway task - newConnectGatewayTask(structs.ConnectMeshPrefix, "my-gateway", false), + newConnectGatewayTask(structs.ConnectMeshPrefix, "my-gateway", false, false), } expTG.Services[0].Name = "my-gateway" expTG.Services[0].PortLabel = "public_port" @@ -611,7 +622,7 @@ func TestJobEndpointConnect_newConnectGatewayTask_host(t *testing.T) { ci.Parallel(t) t.Run("ingress", func(t *testing.T) { - task := newConnectGatewayTask(structs.ConnectIngressPrefix, "foo", true) + task := newConnectGatewayTask(structs.ConnectIngressPrefix, "foo", true, false) require.Equal(t, "connect-ingress-foo", task.Name) require.Equal(t, "connect-ingress:foo", string(task.Kind)) require.Equal(t, ">= 1.8.0", task.Constraints[0].RTarget) @@ -620,7 +631,7 @@ func TestJobEndpointConnect_newConnectGatewayTask_host(t *testing.T) { }) t.Run("terminating", func(t *testing.T) { - task := newConnectGatewayTask(structs.ConnectTerminatingPrefix, "bar", true) + task := newConnectGatewayTask(structs.ConnectTerminatingPrefix, "bar", true, false) require.Equal(t, "connect-terminating-bar", task.Name) require.Equal(t, "connect-terminating:bar", string(task.Kind)) require.Equal(t, ">= 1.8.0", task.Constraints[0].RTarget) @@ -632,7 +643,7 @@ func TestJobEndpointConnect_newConnectGatewayTask_host(t *testing.T) { func TestJobEndpointConnect_newConnectGatewayTask_bridge(t *testing.T) { ci.Parallel(t) - task := newConnectGatewayTask(structs.ConnectIngressPrefix, "service1", false) + task := newConnectGatewayTask(structs.ConnectIngressPrefix, "service1", false, false) require.NotContains(t, task.Config, "network_mode") } diff --git a/nomad/structs/diff.go b/nomad/structs/diff.go index d64ec05a1..8c16123e1 100644 --- a/nomad/structs/diff.go +++ b/nomad/structs/diff.go @@ -1129,9 +1129,11 @@ func connectGatewayTLSConfigDiff(prev, next *ConsulGatewayTLSConfig, contextual if reflect.DeepEqual(prev, next) { return nil } else if prev == nil { + prev = &ConsulGatewayTLSConfig{} diff.Type = DiffTypeAdded newPrimitiveFlat = flatmap.Flatten(next, nil, true) } else if next == nil { + next = &ConsulGatewayTLSConfig{} diff.Type = DiffTypeDeleted oldPrimitiveFlat = flatmap.Flatten(prev, nil, true) } else { @@ -1140,6 +1142,11 @@ func connectGatewayTLSConfigDiff(prev, next *ConsulGatewayTLSConfig, contextual newPrimitiveFlat = flatmap.Flatten(next, nil, true) } + // CipherSuites diffs + if setDiff := stringSetDiff(prev.CipherSuites, next.CipherSuites, "CipherSuites", contextual); setDiff != nil { + diff.Objects = append(diff.Objects, setDiff) + } + // Diff the primitive field. diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) diff --git a/nomad/structs/diff_test.go b/nomad/structs/diff_test.go index 8cb208cb7..1a5751a8c 100644 --- a/nomad/structs/diff_test.go +++ b/nomad/structs/diff_test.go @@ -3265,6 +3265,18 @@ func TestTaskGroupDiff(t *testing.T) { Old: "false", New: "true", }, + { + Type: DiffTypeNone, + Name: "TLSMaxVersion", + Old: "", + New: "", + }, + { + Type: DiffTypeNone, + Name: "TLSMinVersion", + Old: "", + New: "", + }, }, }, { @@ -7893,6 +7905,100 @@ func TestServicesDiff(t *testing.T) { }, }, }, + { + Name: "Service with different ingress tls", + Contextual: false, + Old: []*Service{ + { + Name: "webapp", + Provider: "consul", + PortLabel: "http", + Connect: &ConsulConnect{ + Gateway: &ConsulGateway{ + Ingress: &ConsulIngressConfigEntry{}, + }, + }, + }, + }, + New: []*Service{ + { + Name: "webapp", + Provider: "consul", + PortLabel: "http", + Connect: &ConsulConnect{ + Gateway: &ConsulGateway{ + Ingress: &ConsulIngressConfigEntry{ + TLS: &ConsulGatewayTLSConfig{ + Enabled: true, + TLSMinVersion: "TLSv1_2", + CipherSuites: []string{"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"}, + }, + }, + }, + }, + }, + }, + Expected: []*ObjectDiff{ + { + Type: DiffTypeEdited, + Name: "Service", + Objects: []*ObjectDiff{ + { + Type: DiffTypeEdited, + Name: "ConsulConnect", + Objects: []*ObjectDiff{ + { + Type: DiffTypeEdited, + Name: "Gateway", + Objects: []*ObjectDiff{ + { + Type: DiffTypeEdited, + Name: "Ingress", + Objects: []*ObjectDiff{ + { + Type: DiffTypeAdded, + Name: "TLS", + Fields: []*FieldDiff{ + { + Type: DiffTypeAdded, + Name: "Enabled", + New: "true", + }, + { + Type: DiffTypeAdded, + Name: "TLSMinVersion", + New: "TLSv1_2", + }, + }, + Objects: []*ObjectDiff{ + { + Type: DiffTypeAdded, + Name: "CipherSuites", + Fields: []*FieldDiff{ + { + Type: DiffTypeAdded, + Name: "CipherSuites", + New: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + }, + { + Type: DiffTypeAdded, + Name: "CipherSuites", + New: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, } for _, c := range cases { diff --git a/nomad/structs/services.go b/nomad/structs/services.go index 28d566017..93ac0c96c 100644 --- a/nomad/structs/services.go +++ b/nomad/structs/services.go @@ -928,6 +928,14 @@ func (c *ConsulConnect) IsTerminating() bool { return c.IsGateway() && c.Gateway.Terminating != nil } +// IsCustomizedTLS checks if the service customizes ingress tls config. +func (c *ConsulConnect) IsCustomizedTLS() bool { + return c.IsIngress() && c.Gateway.Ingress.TLS != nil && + (c.Gateway.Ingress.TLS.TLSMinVersion != "" || + c.Gateway.Ingress.TLS.TLSMaxVersion != "" || + len(c.Gateway.Ingress.TLS.CipherSuites) != 0) +} + func (c *ConsulConnect) IsMesh() bool { return c.IsGateway() && c.Gateway.Mesh != nil } @@ -1775,7 +1783,10 @@ func (p *ConsulGatewayProxy) Validate() error { // ConsulGatewayTLSConfig is used to configure TLS for a gateway. type ConsulGatewayTLSConfig struct { - Enabled bool + Enabled bool + TLSMinVersion string + TLSMaxVersion string + CipherSuites []string } func (c *ConsulGatewayTLSConfig) Copy() *ConsulGatewayTLSConfig { @@ -1784,7 +1795,10 @@ func (c *ConsulGatewayTLSConfig) Copy() *ConsulGatewayTLSConfig { } return &ConsulGatewayTLSConfig{ - Enabled: c.Enabled, + Enabled: c.Enabled, + TLSMinVersion: c.TLSMinVersion, + TLSMaxVersion: c.TLSMaxVersion, + CipherSuites: helper.CopySliceString(c.CipherSuites), } } @@ -1793,7 +1807,10 @@ func (c *ConsulGatewayTLSConfig) Equals(o *ConsulGatewayTLSConfig) bool { return c == o } - return c.Enabled == o.Enabled + return c.Enabled == o.Enabled && + c.TLSMinVersion == o.TLSMinVersion && + c.TLSMaxVersion == o.TLSMaxVersion && + helper.CompareSliceSetString(c.CipherSuites, o.CipherSuites) } // ConsulIngressService is used to configure a service fronted by the ingress gateway. diff --git a/website/content/docs/job-specification/gateway.mdx b/website/content/docs/job-specification/gateway.mdx index ef3474ffc..3f88906e5 100644 --- a/website/content/docs/job-specification/gateway.mdx +++ b/website/content/docs/job-specification/gateway.mdx @@ -98,6 +98,21 @@ envoy_gateway_bind_addresses "" { 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`](https://www.consul.io/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`](https://www.consul.io/docs/connect/config-entries/ingress-gateway#tlsmaxversion) + in the Consul documentation for supported versions. + +- `cipher_suites` `(array: optional)` - Set the default list of TLS + cipher suites for the gateway's listeners. Refer to + [`CipherSuites`](https://www.consul.io/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.