mirror of
https://github.com/TwiN/gatus.git
synced 2024-12-14 11:58:04 +00: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
4 changed files with 110 additions and 4 deletions
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 DNS queries](#monitoring-a-service-using-dns-queries)
|
||||
- [Monitoring a service using STARTTLS](#monitoring-a-service-using-starttls)
|
||||
- [Monitoring a service using TLS](#monitoring-a-service-using-tls)
|
||||
- [Basic authentication](#basic-authentication)
|
||||
- [disable-monitoring-lock](#disable-monitoring-lock)
|
||||
- [Reloading configuration on the fly](#reloading-configuration-on-the-fly)
|
||||
|
@ -963,6 +964,24 @@ services:
|
|||
- name: starttls-smtp-example
|
||||
url: "starttls://smtp.gmail.com:587"
|
||||
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:
|
||||
- "[CONNECTED] == true"
|
||||
- "[CERTIFICATE_EXPIRATION] > 48h"
|
||||
|
|
|
@ -38,7 +38,11 @@ func CanPerformStartTLS(address string, config *Config) (connected bool, certifi
|
|||
if len(hostAndPort) != 2 {
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
@ -57,6 +61,28 @@ func CanPerformStartTLS(address string, config *Config) (connected bool, certifi
|
|||
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
|
||||
//
|
||||
// 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) {
|
||||
if CanCreateTCPConnection("127.0.0.1", &Config{Timeout: 5 * time.Second}) {
|
||||
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://")
|
||||
isServiceICMP := strings.HasPrefix(service.URL, "icmp://")
|
||||
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 {
|
||||
request = service.buildHTTPRequest()
|
||||
}
|
||||
|
@ -225,8 +226,18 @@ func (service *Service) call(result *Result) {
|
|||
if isServiceDNS {
|
||||
service.DNS.query(service.URL, result)
|
||||
result.Duration = time.Since(startTime)
|
||||
} else if isServiceStartTLS {
|
||||
result.Connected, certificate, err = client.CanPerformStartTLS(strings.TrimPrefix(service.URL, "starttls://"), service.ClientConfig)
|
||||
} else if isServiceStartTLS || isServiceTLS {
|
||||
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 {
|
||||
result.AddError(err.Error())
|
||||
return
|
||||
|
|
Loading…
Reference in a new issue