Only register HTTPS agent check when Consul>=0.7.2

Support for TLSSkipVerify in other checks coming soon!
This commit is contained in:
Michael Schurter 2017-04-18 21:28:25 -07:00
parent c8d3e869c6
commit 947e31e9c2
4 changed files with 205 additions and 9 deletions

View File

@ -8,6 +8,7 @@ import (
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"sync/atomic"
@ -54,6 +55,10 @@ type Agent struct {
// consulCatalog is the subset of Consul's Catalog API Nomad uses.
consulCatalog consul.CatalogAPI
// consulSupportsTLSSkipVerify flags whether or not Nomad can register
// checks with TLSSkipVerify
consulSupportsTLSSkipVerify bool
client *client.Client
server *nomad.Server
@ -374,6 +379,16 @@ func (a *Agent) setupServer() error {
},
},
}
if conf.TLSConfig.EnableHTTP {
if a.consulSupportsTLSSkipVerify {
httpServ.Checks[0].Protocol = "https"
httpServ.Checks[0].TLSSkipVerify = true
} else {
// No TLSSkipVerify support, don't register https check
a.logger.Printf("[WARN] agent: not registering Nomad HTTPS Health Check because it requires Consul>=0.7.2")
httpServ.Checks = []*structs.ServiceCheck{}
}
}
rpcServ := &structs.Service{
Name: a.config.Consul.ServerServiceName,
PortLabel: a.config.AdvertiseAddrs.RPC,
@ -404,13 +419,10 @@ func (a *Agent) setupServer() error {
}
// Add the http port check if TLS isn't enabled
// TODO Add TLS check when Consul 0.7.1 comes out.
consulServices := []*structs.Service{
rpcServ,
serfServ,
}
if !conf.TLSConfig.EnableHTTP {
consulServices = append(consulServices, httpServ)
httpServ,
}
if err := a.consulService.RegisterAgent(consulRoleServer, consulServices); err != nil {
return err
@ -477,8 +489,6 @@ func (a *Agent) setupClient() error {
}
// Create the Nomad Client services for Consul
// TODO think how we can re-introduce HTTP/S checks when Consul 0.7.1 comes
// out
if *a.config.Consul.AutoAdvertise {
httpServ := &structs.Service{
Name: a.config.Consul.ClientServiceName,
@ -496,11 +506,19 @@ func (a *Agent) setupClient() error {
},
},
}
if !conf.TLSConfig.EnableHTTP {
if err := a.consulService.RegisterAgent(consulRoleClient, []*structs.Service{httpServ}); err != nil {
return err
if conf.TLSConfig.EnableHTTP {
if a.consulSupportsTLSSkipVerify {
httpServ.Checks[0].Protocol = "https"
httpServ.Checks[0].TLSSkipVerify = true
} else {
// No TLSSkipVerify support, don't register https check
a.logger.Printf("[WARN] agent: not registering Nomad HTTPS Health Check because it requires Consul>=0.7.2")
httpServ.Checks = []*structs.ServiceCheck{}
}
}
if err := a.consulService.RegisterAgent(consulRoleClient, []*structs.Service{httpServ}); err != nil {
return err
}
}
return nil
@ -672,6 +690,11 @@ func (a *Agent) setupConsul(consulConfig *config.ConsulConfig) error {
return err
}
// Determine version for TLSSkipVerify
if self, err := client.Agent().Self(); err != nil {
a.consulSupportsTLSSkipVerify = consulSupportsTLSSkipVerify(self)
}
// Create Consul Catalog client for service discovery.
a.consulCatalog = client.Catalog()
@ -680,3 +703,59 @@ func (a *Agent) setupConsul(consulConfig *config.ConsulConfig) error {
go a.consulService.Run()
return nil
}
// consulSupportsTLSSkipVerify returns true if Consul supports TLSSkipVerify.
func consulSupportsTLSSkipVerify(self map[string]map[string]interface{}) bool {
member, ok := self["Member"]
if !ok {
return false
}
tagsI, ok := member["Tags"]
if !ok {
return false
}
tags, ok := tagsI.(map[string]interface{})
if !ok {
return false
}
buildI, ok := tags["build"]
if !ok {
return false
}
build, ok := buildI.(string)
if !ok {
return false
}
parts := strings.SplitN(build, ":", 2)
if len(parts) == 0 {
return false
}
parts = strings.Split(parts[0], ".")
if len(parts) != 3 {
return false
}
major, err := strconv.Atoi(parts[0])
if err != nil {
return false
}
minor, err := strconv.Atoi(parts[1])
if err != nil {
return false
}
patch, err := strconv.Atoi(parts[2])
if err != nil {
return false
}
if major > 0 || minor > 7 {
// After 0.7.2!
return true
}
if minor < 7 {
return false
}
if patch < 2 {
return false
}
// 0.7.2 or higher!
return true
}

View File

@ -1,6 +1,7 @@
package agent
import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
@ -358,3 +359,107 @@ func TestAgent_ClientConfig(t *testing.T) {
t.Fatalf("Expected http addr: %v, got: %v", expectedHttpAddr, c.Node.HTTPAddr)
}
}
func TestAgent_ConsulSupportsTLSSkipVerify(t *testing.T) {
assertSupport := func(expected bool, blob string) {
self := map[string]map[string]interface{}{}
if err := json.Unmarshal([]byte("{"+blob+"}"), &self); err != nil {
t.Fatalf("invalid json: %v", err)
}
actual := consulSupportsTLSSkipVerify(self)
if actual != expected {
t.Errorf("expected %t but got %t for:\n%s\n", expected, actual, blob)
}
}
// 0.6.4
assertSupport(false, `"Member": {
"Addr": "127.0.0.1",
"DelegateCur": 4,
"DelegateMax": 4,
"DelegateMin": 2,
"Name": "rusty",
"Port": 8301,
"ProtocolCur": 2,
"ProtocolMax": 3,
"ProtocolMin": 1,
"Status": 1,
"Tags": {
"build": "0.6.4:26a0ef8c",
"dc": "dc1",
"port": "8300",
"role": "consul",
"vsn": "2",
"vsn_max": "3",
"vsn_min": "1"
}}`)
// 0.7.0
assertSupport(false, `"Member": {
"Addr": "127.0.0.1",
"DelegateCur": 4,
"DelegateMax": 4,
"DelegateMin": 2,
"Name": "rusty",
"Port": 8301,
"ProtocolCur": 2,
"ProtocolMax": 4,
"ProtocolMin": 1,
"Status": 1,
"Tags": {
"build": "0.7.0:'a189091",
"dc": "dc1",
"port": "8300",
"role": "consul",
"vsn": "2",
"vsn_max": "3",
"vsn_min": "2"
}}`)
// 0.7.2
assertSupport(true, `"Member": {
"Addr": "127.0.0.1",
"DelegateCur": 4,
"DelegateMax": 4,
"DelegateMin": 2,
"Name": "rusty",
"Port": 8301,
"ProtocolCur": 2,
"ProtocolMax": 5,
"ProtocolMin": 1,
"Status": 1,
"Tags": {
"build": "0.7.2:'a9afa0c",
"dc": "dc1",
"port": "8300",
"role": "consul",
"vsn": "2",
"vsn_max": "3",
"vsn_min": "2"
}}`)
// 0.8.1
assertSupport(true, `"Member": {
"Addr": "127.0.0.1",
"DelegateCur": 4,
"DelegateMax": 5,
"DelegateMin": 2,
"Name": "rusty",
"Port": 8301,
"ProtocolCur": 2,
"ProtocolMax": 5,
"ProtocolMin": 1,
"Status": 1,
"Tags": {
"build": "0.8.1:'e9ca44d",
"dc": "dc1",
"id": "3ddc1b59-460e-a100-1d5c-ce3972122664",
"port": "8300",
"raft_vsn": "2",
"role": "consul",
"vsn": "2",
"vsn_max": "3",
"vsn_min": "2",
"wan_join_port": "8302"
}}`)
}

View File

@ -666,6 +666,9 @@ func createCheckReg(serviceID, checkID string, check *structs.ServiceCheck, host
if check.Protocol == "" {
check.Protocol = "http"
}
if check.TLSSkipVerify {
chkReg.TLSSkipVerify = true
}
base := url.URL{
Scheme: check.Protocol,
Host: net.JoinHostPort(host, strconv.Itoa(port)),

View File

@ -2116,6 +2116,7 @@ type ServiceCheck struct {
Interval time.Duration // Interval of the check
Timeout time.Duration // Timeout of the response from the check before consul fails the check
InitialStatus string // Initial status of the check
TLSSkipVerify bool // Skip TLS verification when Protocol=https
}
func (sc *ServiceCheck) Copy() *ServiceCheck {
@ -2199,6 +2200,10 @@ func (sc *ServiceCheck) RequiresPort() bool {
}
}
// Hash all ServiceCheck fields and the check's corresponding service ID to
// create an identifier. The identifier is not guaranteed to be unique as if
// the PortLabel is blank, the Service's PortLabel will be used after Hash is
// called.
func (sc *ServiceCheck) Hash(serviceID string) string {
h := sha1.New()
io.WriteString(h, serviceID)
@ -2211,6 +2216,10 @@ func (sc *ServiceCheck) Hash(serviceID string) string {
io.WriteString(h, sc.PortLabel)
io.WriteString(h, sc.Interval.String())
io.WriteString(h, sc.Timeout.String())
// Only include TLSSkipVerify if set to maintain ID stability with Nomad <0.6
if sc.TLSSkipVerify {
io.WriteString(h, "true")
}
return fmt.Sprintf("%x", h.Sum(nil))
}