check: Add support for Consul field tls_server_name (#17334)

This commit is contained in:
Samantha 2023-06-02 10:19:12 -04:00 committed by GitHub
parent 56e9b944e8
commit b92a782b6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 79 additions and 5 deletions

3
.changelog/17334.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
checks: Added support for Consul check field tls_server_name
```

View File

@ -212,6 +212,7 @@ type ServiceCheck struct {
Interval time.Duration `hcl:"interval,optional"` Interval time.Duration `hcl:"interval,optional"`
Timeout time.Duration `hcl:"timeout,optional"` Timeout time.Duration `hcl:"timeout,optional"`
InitialStatus string `mapstructure:"initial_status" hcl:"initial_status,optional"` InitialStatus string `mapstructure:"initial_status" hcl:"initial_status,optional"`
TLSServerName string `mapstructure:"tls_server_name" hcl:"tls_server_name,optional"`
TLSSkipVerify bool `mapstructure:"tls_skip_verify" hcl:"tls_skip_verify,optional"` TLSSkipVerify bool `mapstructure:"tls_skip_verify" hcl:"tls_skip_verify,optional"`
Header map[string][]string `hcl:"header,block"` Header map[string][]string `hcl:"header,block"`
Method string `hcl:"method,optional"` Method string `hcl:"method,optional"`

View File

@ -1164,6 +1164,7 @@ func apiCheckRegistrationToCheck(r *api.AgentCheckRegistration) *api.AgentServic
Body: r.Body, Body: r.Body,
TCP: r.TCP, TCP: r.TCP,
Status: r.Status, Status: r.Status,
TLSServerName: r.TLSServerName,
TLSSkipVerify: r.TLSSkipVerify, TLSSkipVerify: r.TLSSkipVerify,
GRPC: r.GRPC, GRPC: r.GRPC,
GRPCUseTLS: r.GRPCUseTLS, GRPCUseTLS: r.GRPCUseTLS,
@ -1653,6 +1654,7 @@ func createCheckReg(serviceID, checkID string, check *structs.ServiceCheck, host
if check.TLSSkipVerify { if check.TLSSkipVerify {
chkReg.TLSSkipVerify = true chkReg.TLSSkipVerify = true
} }
chkReg.TLSServerName = check.TLSServerName
base := url.URL{ base := url.URL{
Scheme: proto, Scheme: proto,
Host: net.JoinHostPort(host, strconv.Itoa(port)), Host: net.JoinHostPort(host, strconv.Itoa(port)),
@ -1681,6 +1683,7 @@ func createCheckReg(serviceID, checkID string, check *structs.ServiceCheck, host
if check.TLSSkipVerify { if check.TLSSkipVerify {
chkReg.TLSSkipVerify = true chkReg.TLSSkipVerify = true
} }
chkReg.TLSServerName = check.TLSServerName
default: default:
return nil, fmt.Errorf("check type %+q not valid", check.Type) return nil, fmt.Errorf("check type %+q not valid", check.Type)

View File

@ -972,6 +972,7 @@ func TestCreateCheckReg_GRPC(t *testing.T) {
PortLabel: "label", PortLabel: "label",
GRPCService: "foo.Bar", GRPCService: "foo.Bar",
GRPCUseTLS: true, GRPCUseTLS: true,
TLSServerName: "localhost",
TLSSkipVerify: true, TLSSkipVerify: true,
Timeout: time.Second, Timeout: time.Second,
Interval: time.Minute, Interval: time.Minute,
@ -988,13 +989,14 @@ func TestCreateCheckReg_GRPC(t *testing.T) {
AgentServiceCheck: api.AgentServiceCheck{ AgentServiceCheck: api.AgentServiceCheck{
Timeout: "1s", Timeout: "1s",
Interval: "1m0s", Interval: "1m0s",
GRPC: "localhost:8080/foo.Bar", GRPC: "127.0.0.1:8080/foo.Bar",
GRPCUseTLS: true, GRPCUseTLS: true,
TLSServerName: "localhost",
TLSSkipVerify: true, TLSSkipVerify: true,
}, },
} }
actual, err := createCheckReg(serviceID, checkID, check, "localhost", 8080, "default") actual, err := createCheckReg(serviceID, checkID, check, "127.0.0.1", 8080, "default")
must.NoError(t, err) must.NoError(t, err)
must.Eq(t, expected, actual) must.Eq(t, expected, actual)
} }

View File

@ -1484,6 +1484,7 @@ func ApiServicesToStructs(in []*api.Service, group bool) []*structs.Service {
Interval: check.Interval, Interval: check.Interval,
Timeout: check.Timeout, Timeout: check.Timeout,
InitialStatus: check.InitialStatus, InitialStatus: check.InitialStatus,
TLSServerName: check.TLSServerName,
TLSSkipVerify: check.TLSSkipVerify, TLSSkipVerify: check.TLSSkipVerify,
Header: check.Header, Header: check.Header,
Method: check.Method, Method: check.Method,

View File

@ -3085,6 +3085,12 @@ func TestTaskGroupDiff(t *testing.T) {
Old: "3", Old: "3",
New: "5", New: "5",
}, },
{
Type: DiffTypeNone,
Name: "TLSServerName",
Old: "",
New: "",
},
{ {
Type: DiffTypeNone, Type: DiffTypeNone,
Name: "TLSSkipVerify", Name: "TLSSkipVerify",
@ -6625,6 +6631,12 @@ func TestTaskDiff(t *testing.T) {
Old: "4", Old: "4",
New: "4", New: "4",
}, },
{
Type: DiffTypeNone,
Name: "TLSServerName",
Old: "",
New: "",
},
{ {
Type: DiffTypeNone, Type: DiffTypeNone,
Name: "TLSSkipVerify", Name: "TLSSkipVerify",

View File

@ -68,7 +68,8 @@ type ServiceCheck struct {
Interval time.Duration // Interval of the check Interval time.Duration // Interval of the check
Timeout time.Duration // Timeout of the response from the check before consul fails the check Timeout time.Duration // Timeout of the response from the check before consul fails the check
InitialStatus string // Initial status of the check InitialStatus string // Initial status of the check
TLSSkipVerify bool // Skip TLS verification when Protocol=https TLSServerName string // ServerName to use for SNI and TLS verification when (Type=https and Protocol=https) or (Type=grpc and GRPCUseTLS=true)
TLSSkipVerify bool // Skip TLS verification when (type=https and Protocol=https) or (type=grpc and grpc_use_tls=true)
Method string // HTTP Method to use (GET by default) Method string // HTTP Method to use (GET by default)
Header map[string][]string // HTTP Headers for Consul to set when making HTTP checks Header map[string][]string // HTTP Headers for Consul to set when making HTTP checks
CheckRestart *CheckRestart // If and when a task should be restarted based on checks CheckRestart *CheckRestart // If and when a task should be restarted based on checks
@ -183,6 +184,10 @@ func (sc *ServiceCheck) Equal(o *ServiceCheck) bool {
return false return false
} }
if sc.TLSServerName != o.TLSServerName {
return false
}
if sc.Timeout != o.Timeout { if sc.Timeout != o.Timeout {
return false return false
} }
@ -378,6 +383,11 @@ func (sc *ServiceCheck) validateNomad() error {
return fmt.Errorf("failures_before_critical may only be set for Consul service checks") return fmt.Errorf("failures_before_critical may only be set for Consul service checks")
} }
// tls_server_name is consul only
if sc.TLSServerName != "" {
return fmt.Errorf("tls_server_name may only be set for Consul service checks")
}
return nil return nil
} }
@ -465,6 +475,9 @@ func (sc *ServiceCheck) Hash(serviceID string) string {
// use name "true" to maintain ID stability // use name "true" to maintain ID stability
hashBool(h, sc.TLSSkipVerify, "true") hashBool(h, sc.TLSSkipVerify, "true")
// Only include TLSServerName if set to maintain ID stability with Nomad <1.6.0
hashStringIfNonEmpty(h, sc.TLSServerName)
// maintain artisanal map hashing to maintain ID stability // maintain artisanal map hashing to maintain ID stability
hashHeader(h, sc.Header) hashHeader(h, sc.Header)

View File

@ -353,6 +353,17 @@ func TestServiceCheck_validateNomad(t *testing.T) {
Body: "this is a request payload!", Body: "this is a request payload!",
}, },
}, },
{
name: "http with tls_server_name",
sc: &ServiceCheck{
Type: ServiceCheckHTTP,
Interval: 3 * time.Second,
Timeout: 1 * time.Second,
Path: "/health",
TLSServerName: "foo",
},
exp: `tls_server_name may only be set for Consul service checks`,
},
} }
for _, testCase := range testCases { for _, testCase := range testCases {

View File

@ -434,6 +434,13 @@ export let DiffViewerWithManyChanges = () => {
Old: '', Old: '',
Type: 'None', Type: 'None',
}, },
{
Annotations: null,
Name: 'TLSServerName',
New: '',
Old: '',
Type: 'None',
},
{ {
Annotations: null, Annotations: null,
Name: 'TLSSkipVerify', Name: 'TLSSkipVerify',

View File

@ -81,6 +81,8 @@ job "example" {
- `grpc_use_tls` `(bool: false)` - Use TLS to perform a gRPC health check. May - `grpc_use_tls` `(bool: false)` - Use TLS to perform a gRPC health check. May
be used with `tls_skip_verify` to use TLS but skip certificate verification. be used with `tls_skip_verify` to use TLS but skip certificate verification.
May be used with `tls_server_name` to specify the ServerName to use for SNI
and validation of the certificate presented by the server being checked.
- `initial_status` `(string: <enum>)` - Specifies the starting status of the - `initial_status` `(string: <enum>)` - Specifies the starting status of the
service. Valid options are `passing`, `warning`, and `critical`. Omitting service. Valid options are `passing`, `warning`, and `critical`. Omitting
@ -157,8 +159,27 @@ job "example" {
Nomad. For Consul service checks, valid options are `grpc`, `http`, `script`, Nomad. For Consul service checks, valid options are `grpc`, `http`, `script`,
and `tcp`. For Nomad service checks, valid options are `http` and `tcp`. and `tcp`. For Nomad service checks, valid options are `http` and `tcp`.
- `tls_skip_verify` `(bool: false)` - Skip verifying TLS certificates for HTTPS - `tls_server_name` `(string: "")` - Indicates the ServerName to use for SNI and
checks. Only supported in the Consul service provider. validation of the certificate presented by the server being checked, when
performing TLS enabled checks (`https` and `grpc` with `grpc_use_tls`). If left
unspecified, the ServerName will be inferred from the address of the server
being checked unless the address is an IP address. There are two common cases
where this is beneficial:
- When the check address contains an IP, `tls_server_name` can be specified
for SNI. Note: setting `tls_server_name` will also override the hostname
used to verify the certificate presented by the server being checked.
- When the check address contains a hostname which isn't be present in the
SAN (Subject Alternative Name) field of the certificate presented by the
server being checked. Note: setting `tls_server_name` will also override
the hostname used for SNI.
This field is only supported in the Consul service provider.
- `tls_skip_verify` `(bool: false)` - Skip verification of certificates for
`https` and `grpc` with `grpc_use_tls` checks . Only supported in the Consul
service provider.
- `on_update` `(string: "require_healthy")` - Specifies how checks should be - `on_update` `(string: "require_healthy")` - Specifies how checks should be
evaluated when determining deployment health (including a job's initial evaluated when determining deployment health (including a job's initial