mirror of
https://github.com/TwiN/gatus.git
synced 2024-11-24 17:04:42 +01:00
Health check for SSL/TLS services (#177)
* protocol: starttls: add timeout support Signed-off-by: Charles Decoux <charles@phowork.fr> * protocol: add ssl support Signed-off-by: Charles Decoux <charles@phowork.fr>
This commit is contained in:
parent
289d834587
commit
30cb7b6ec8
19
README.md
19
README.md
@ -62,6 +62,7 @@ For more details, see [Usage](#usage)
|
|||||||
- [Monitoring a service using ICMP](#monitoring-a-service-using-icmp)
|
- [Monitoring a service using ICMP](#monitoring-a-service-using-icmp)
|
||||||
- [Monitoring a service using DNS queries](#monitoring-a-service-using-dns-queries)
|
- [Monitoring a service using DNS queries](#monitoring-a-service-using-dns-queries)
|
||||||
- [Monitoring a service using STARTTLS](#monitoring-a-service-using-starttls)
|
- [Monitoring a service using STARTTLS](#monitoring-a-service-using-starttls)
|
||||||
|
- [Monitoring a service using TLS](#monitoring-a-service-using-tls)
|
||||||
- [Basic authentication](#basic-authentication)
|
- [Basic authentication](#basic-authentication)
|
||||||
- [disable-monitoring-lock](#disable-monitoring-lock)
|
- [disable-monitoring-lock](#disable-monitoring-lock)
|
||||||
- [Reloading configuration on the fly](#reloading-configuration-on-the-fly)
|
- [Reloading configuration on the fly](#reloading-configuration-on-the-fly)
|
||||||
@ -963,6 +964,24 @@ services:
|
|||||||
- name: starttls-smtp-example
|
- name: starttls-smtp-example
|
||||||
url: "starttls://smtp.gmail.com:587"
|
url: "starttls://smtp.gmail.com:587"
|
||||||
interval: 30m
|
interval: 30m
|
||||||
|
client:
|
||||||
|
timeout: 5s
|
||||||
|
conditions:
|
||||||
|
- "[CONNECTED] == true"
|
||||||
|
- "[CERTIFICATE_EXPIRATION] > 48h"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Monitoring a service using TLS
|
||||||
|
Monitoring services using SSL/TLS encryption, such as LDAP over TLS, can help
|
||||||
|
detecting certificate expiration:
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
- name: tls-ldaps-example
|
||||||
|
url: "tls://ldap.example.com:636"
|
||||||
|
interval: 30m
|
||||||
|
client:
|
||||||
|
timeout: 5s
|
||||||
conditions:
|
conditions:
|
||||||
- "[CONNECTED] == true"
|
- "[CONNECTED] == true"
|
||||||
- "[CERTIFICATE_EXPIRATION] > 48h"
|
- "[CERTIFICATE_EXPIRATION] > 48h"
|
||||||
|
@ -38,7 +38,11 @@ func CanPerformStartTLS(address string, config *Config) (connected bool, certifi
|
|||||||
if len(hostAndPort) != 2 {
|
if len(hostAndPort) != 2 {
|
||||||
return false, nil, errors.New("invalid address for starttls, format must be host:port")
|
return false, nil, errors.New("invalid address for starttls, format must be host:port")
|
||||||
}
|
}
|
||||||
smtpClient, err := smtp.Dial(address)
|
conn, err := net.DialTimeout("tcp", address, config.Timeout)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
smtpClient, err := smtp.NewClient(conn, hostAndPort[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -57,6 +61,28 @@ func CanPerformStartTLS(address string, config *Config) (connected bool, certifi
|
|||||||
return true, certificate, nil
|
return true, certificate, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CanPerformTLS checks whether a connection can be established to an address using the TLS protocol
|
||||||
|
func CanPerformTLS(address string, config *Config) (connected bool, certificate *x509.Certificate, err error) {
|
||||||
|
conn, err := tls.DialWithDialer(&net.Dialer{Timeout: config.Timeout}, "tcp", address, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
verifiedChains := conn.ConnectionState().VerifiedChains
|
||||||
|
if len(verifiedChains) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
chain := verifiedChains[0] // VerifiedChains[0] == PeerCertificates[0]
|
||||||
|
if len(chain) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
certificate = chain[0]
|
||||||
|
return true, certificate, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Ping checks if an address can be pinged and returns the round-trip time if the address can be pinged
|
// Ping checks if an address can be pinged and returns the round-trip time if the address can be pinged
|
||||||
//
|
//
|
||||||
// Note that this function takes at least 100ms, even if the address is 127.0.0.1
|
// Note that this function takes at least 100ms, even if the address is 127.0.0.1
|
||||||
|
@ -91,6 +91,56 @@ func TestCanPerformStartTLS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCanPerformTLS(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
address string
|
||||||
|
insecure bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantConnected bool
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "invalid address",
|
||||||
|
args: args{
|
||||||
|
address: "test",
|
||||||
|
},
|
||||||
|
wantConnected: false,
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "error dial",
|
||||||
|
args: args{
|
||||||
|
address: "test:1234",
|
||||||
|
},
|
||||||
|
wantConnected: false,
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid tls",
|
||||||
|
args: args{
|
||||||
|
address: "smtp.gmail.com:465",
|
||||||
|
},
|
||||||
|
wantConnected: true,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
connected, _, err := CanPerformTLS(tt.args.address, &Config{Insecure: tt.args.insecure, Timeout: 5 * time.Second})
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("CanPerformTLS() err=%v, wantErr=%v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if connected != tt.wantConnected {
|
||||||
|
t.Errorf("CanPerformTLS() connected=%v, wantConnected=%v", connected, tt.wantConnected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCanCreateTCPConnection(t *testing.T) {
|
func TestCanCreateTCPConnection(t *testing.T) {
|
||||||
if CanCreateTCPConnection("127.0.0.1", &Config{Timeout: 5 * time.Second}) {
|
if CanCreateTCPConnection("127.0.0.1", &Config{Timeout: 5 * time.Second}) {
|
||||||
t.Error("should've failed, because there's no port in the address")
|
t.Error("should've failed, because there's no port in the address")
|
||||||
|
@ -217,7 +217,8 @@ func (service *Service) call(result *Result) {
|
|||||||
isServiceTCP := strings.HasPrefix(service.URL, "tcp://")
|
isServiceTCP := strings.HasPrefix(service.URL, "tcp://")
|
||||||
isServiceICMP := strings.HasPrefix(service.URL, "icmp://")
|
isServiceICMP := strings.HasPrefix(service.URL, "icmp://")
|
||||||
isServiceStartTLS := strings.HasPrefix(service.URL, "starttls://")
|
isServiceStartTLS := strings.HasPrefix(service.URL, "starttls://")
|
||||||
isServiceHTTP := !isServiceDNS && !isServiceTCP && !isServiceICMP && !isServiceStartTLS
|
isServiceTLS := strings.HasPrefix(service.URL, "tls://")
|
||||||
|
isServiceHTTP := !isServiceDNS && !isServiceTCP && !isServiceICMP && !isServiceStartTLS && !isServiceTLS
|
||||||
if isServiceHTTP {
|
if isServiceHTTP {
|
||||||
request = service.buildHTTPRequest()
|
request = service.buildHTTPRequest()
|
||||||
}
|
}
|
||||||
@ -225,8 +226,18 @@ func (service *Service) call(result *Result) {
|
|||||||
if isServiceDNS {
|
if isServiceDNS {
|
||||||
service.DNS.query(service.URL, result)
|
service.DNS.query(service.URL, result)
|
||||||
result.Duration = time.Since(startTime)
|
result.Duration = time.Since(startTime)
|
||||||
} else if isServiceStartTLS {
|
} else if isServiceStartTLS || isServiceTLS {
|
||||||
result.Connected, certificate, err = client.CanPerformStartTLS(strings.TrimPrefix(service.URL, "starttls://"), service.ClientConfig)
|
var clientFunction func(address string, config *client.Config) (connected bool, certificate *x509.Certificate, err error)
|
||||||
|
var addressPrefix string
|
||||||
|
if isServiceStartTLS {
|
||||||
|
clientFunction = client.CanPerformStartTLS
|
||||||
|
addressPrefix = "starttls://"
|
||||||
|
} else if isServiceTLS {
|
||||||
|
clientFunction = client.CanPerformTLS
|
||||||
|
addressPrefix = "tls://"
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Connected, certificate, err = clientFunction(strings.TrimPrefix(service.URL, addressPrefix), service.ClientConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.AddError(err.Error())
|
result.AddError(err.Error())
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user