mirror of
https://github.com/ribbybibby/ssl_exporter.git
synced 2024-11-24 08:22:17 +02:00
Add starttls for smtp, imap and ftp (#36)
This commit is contained in:
parent
1c8bd16057
commit
89eff28fac
12
README.md
12
README.md
@ -31,6 +31,8 @@ meaningful visualisations and consoles.
|
||||
- [Configuration file](#configuration-file)
|
||||
- [<module>](#module)
|
||||
- [<tls_config>](#tls_config)
|
||||
- [<https_probe>](#https_probe)
|
||||
- [<tcp_probe>](#tcp_probe)
|
||||
- [Example Queries](#example-queries)
|
||||
- [Proxying](#proxying)
|
||||
- [Grafana](#grafana)
|
||||
@ -152,7 +154,7 @@ modules: [<module>]
|
||||
#### \<module\>
|
||||
|
||||
```
|
||||
# The protocol over which the probe will take place (http, tcp)
|
||||
# The protocol over which the probe will take place (https, tcp)
|
||||
prober: <prober_string>
|
||||
|
||||
# Configuration for TLS
|
||||
@ -160,6 +162,7 @@ prober: <prober_string>
|
||||
|
||||
# The specific probe configuration
|
||||
[ https: <https_probe> ]
|
||||
[ tcp: <tcp_probe> ]
|
||||
```
|
||||
|
||||
#### <tls_config>
|
||||
@ -188,6 +191,13 @@ prober: <prober_string>
|
||||
[ proxy_url: <string> ]
|
||||
```
|
||||
|
||||
#### <tcp_probe>
|
||||
|
||||
```
|
||||
# Use the STARTTLS command before starting TLS for those protocols that support it (smtp, ftp, imap)
|
||||
[ starttls: <string> ]
|
||||
```
|
||||
|
||||
## Example Queries
|
||||
|
||||
Certificates that expire within 7 days:
|
||||
|
@ -52,6 +52,11 @@ type Module struct {
|
||||
Prober string `yaml:"prober,omitempty"`
|
||||
TLSConfig config.TLSConfig `yaml:"tls_config,omitempty"`
|
||||
HTTPS HTTPSProbe `yaml:"https,omitempty"`
|
||||
TCP TCPProbe `yaml:"tcp,omitempty"`
|
||||
}
|
||||
|
||||
type TCPProbe struct {
|
||||
StartTLS string `yaml:"starttls,omitempty"`
|
||||
}
|
||||
|
||||
type HTTPSProbe struct {
|
||||
|
@ -21,3 +21,7 @@ modules:
|
||||
ca_file: /etc/tls/ca.crt
|
||||
cert_file: /etc/tls/tls.crt
|
||||
key_file: /etc/tls/tls.key
|
||||
tcp_smtp_starttls:
|
||||
prober: tcp
|
||||
tcp:
|
||||
starttls: smtp
|
||||
|
3
go.sum
3
go.sum
@ -24,6 +24,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
@ -69,6 +70,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@ -167,6 +169,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
149
prober/tcp.go
149
prober/tcp.go
@ -1,29 +1,166 @@
|
||||
package prober
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/ribbybibby/ssl_exporter/config"
|
||||
|
||||
pconfig "github.com/prometheus/common/config"
|
||||
"github.com/prometheus/common/log"
|
||||
)
|
||||
|
||||
// ProbeTCP performs a tcp probe
|
||||
func ProbeTCP(target string, module config.Module, timeout time.Duration) (*tls.ConnectionState, error) {
|
||||
tlsConfig, err := pconfig.NewTLSConfig(&module.TLSConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dialer := &net.Dialer{Timeout: timeout}
|
||||
|
||||
conn, err := tls.DialWithDialer(&net.Dialer{Timeout: timeout}, "tcp", target, tlsConfig)
|
||||
conn, err := dialer.Dial("tcp", target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
state := conn.ConnectionState()
|
||||
if err := conn.SetDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return nil, fmt.Errorf("Error setting deadline")
|
||||
}
|
||||
|
||||
if module.TCP.StartTLS != "" {
|
||||
err = startTLS(conn, module.TCP.StartTLS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
tlsConfig, err := pconfig.NewTLSConfig(&module.TLSConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tlsConfig.ServerName == "" {
|
||||
targetAddress, _, err := net.SplitHostPort(target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig.ServerName = targetAddress
|
||||
}
|
||||
|
||||
tlsConn := tls.Client(conn, tlsConfig)
|
||||
defer tlsConn.Close()
|
||||
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
state := tlsConn.ConnectionState()
|
||||
|
||||
return &state, nil
|
||||
}
|
||||
|
||||
type queryResponse struct {
|
||||
expect string
|
||||
send string
|
||||
}
|
||||
|
||||
var (
|
||||
// These are the protocols for which I had servers readily available to test
|
||||
// against. There are plenty of other protocols that should be added here in
|
||||
// the future.
|
||||
//
|
||||
// See openssl s_client for more examples:
|
||||
// https://github.com/openssl/openssl/blob/openssl-3.0.0-alpha3/apps/s_client.c#L2229-L2728
|
||||
startTLSqueryResponses = map[string][]queryResponse{
|
||||
"smtp": []queryResponse{
|
||||
queryResponse{
|
||||
expect: "^220",
|
||||
},
|
||||
queryResponse{
|
||||
send: "EHLO prober",
|
||||
},
|
||||
queryResponse{
|
||||
expect: "^250-STARTTLS",
|
||||
},
|
||||
queryResponse{
|
||||
send: "STARTTLS",
|
||||
},
|
||||
queryResponse{
|
||||
expect: "^220",
|
||||
},
|
||||
},
|
||||
"ftp": []queryResponse{
|
||||
queryResponse{
|
||||
expect: "^220",
|
||||
},
|
||||
queryResponse{
|
||||
send: "AUTH TLS",
|
||||
},
|
||||
queryResponse{
|
||||
expect: "^234",
|
||||
},
|
||||
},
|
||||
"imap": []queryResponse{
|
||||
queryResponse{
|
||||
expect: "OK",
|
||||
},
|
||||
queryResponse{
|
||||
send: ". CAPABILITY",
|
||||
},
|
||||
queryResponse{
|
||||
expect: "STARTTLS",
|
||||
},
|
||||
queryResponse{
|
||||
expect: "OK",
|
||||
},
|
||||
queryResponse{
|
||||
send: ". STARTTLS",
|
||||
},
|
||||
queryResponse{
|
||||
expect: "OK",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// startTLS will send the STARTTLS command for the given protocol
|
||||
func startTLS(conn net.Conn, proto string) error {
|
||||
var err error
|
||||
|
||||
qr, ok := startTLSqueryResponses[proto]
|
||||
if !ok {
|
||||
return fmt.Errorf("STARTTLS is not supported for %s", proto)
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(conn)
|
||||
for _, qr := range qr {
|
||||
if qr.expect != "" {
|
||||
var match bool
|
||||
for scanner.Scan() {
|
||||
log.Debugf("read line: %s", scanner.Text())
|
||||
match, err = regexp.Match(qr.expect, scanner.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if match {
|
||||
log.Debugf("regex: %s matched: %s", qr.expect, scanner.Text())
|
||||
break
|
||||
}
|
||||
}
|
||||
if scanner.Err() != nil {
|
||||
return scanner.Err()
|
||||
}
|
||||
if !match {
|
||||
return fmt.Errorf("regex: %s didn't match: %s", qr.expect, scanner.Text())
|
||||
}
|
||||
}
|
||||
if qr.send != "" {
|
||||
log.Debugf("sending line: %s", qr.send)
|
||||
if _, err := fmt.Fprintf(conn, "%s\r\n", qr.send); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -154,3 +154,81 @@ func TestProbeTCPExpiredInsecure(t *testing.T) {
|
||||
t.Fatalf("expected state but got nil")
|
||||
}
|
||||
}
|
||||
|
||||
// TestProbeTCPStartTLSSMTP tests STARTTLS against a mock SMTP server
|
||||
func TestProbeTCPStartTLSSMTP(t *testing.T) {
|
||||
server, _, _, caFile, teardown, err := test.SetupTCPServer()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
defer teardown()
|
||||
|
||||
server.StartSMTP()
|
||||
defer server.Close()
|
||||
|
||||
module := config.Module{
|
||||
TCP: config.TCPProbe{
|
||||
StartTLS: "smtp",
|
||||
},
|
||||
TLSConfig: pconfig.TLSConfig{
|
||||
CAFile: caFile,
|
||||
InsecureSkipVerify: false,
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := ProbeTCP(server.Listener.Addr().String(), module, 10*time.Second); err != nil {
|
||||
t.Fatalf("error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestProbeTCPStartTLSFTP tests STARTTLS against a mock FTP server
|
||||
func TestProbeTCPStartTLSFTP(t *testing.T) {
|
||||
server, _, _, caFile, teardown, err := test.SetupTCPServer()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
defer teardown()
|
||||
|
||||
server.StartFTP()
|
||||
defer server.Close()
|
||||
|
||||
module := config.Module{
|
||||
TCP: config.TCPProbe{
|
||||
StartTLS: "ftp",
|
||||
},
|
||||
TLSConfig: pconfig.TLSConfig{
|
||||
CAFile: caFile,
|
||||
InsecureSkipVerify: false,
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := ProbeTCP(server.Listener.Addr().String(), module, 10*time.Second); err != nil {
|
||||
t.Fatalf("error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestProbeTCPStartTLSIMAP tests STARTTLS against a mock IMAP server
|
||||
func TestProbeTCPStartTLSIMAP(t *testing.T) {
|
||||
server, _, _, caFile, teardown, err := test.SetupTCPServer()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
defer teardown()
|
||||
|
||||
server.StartIMAP()
|
||||
defer server.Close()
|
||||
|
||||
module := config.Module{
|
||||
TCP: config.TCPProbe{
|
||||
StartTLS: "imap",
|
||||
},
|
||||
TLSConfig: pconfig.TLSConfig{
|
||||
CAFile: caFile,
|
||||
InsecureSkipVerify: false,
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := ProbeTCP(server.Listener.Addr().String(), module, 10*time.Second); err != nil {
|
||||
t.Fatalf("error: %s", err)
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,7 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
||||
|
||||
state, err := e.prober(e.target, e.module, e.timeout)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
tlsConnectSuccess, prometheus.GaugeValue, 0,
|
||||
)
|
||||
|
@ -57,19 +57,9 @@ func TestProbeHandlerHTTPS(t *testing.T) {
|
||||
}
|
||||
|
||||
// Check notAfter and notBefore metrics
|
||||
block, _ := pem.Decode(certPEM)
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
if err := checkDates(certPEM, rr.Body.String()); err != nil {
|
||||
t.Errorf(err.Error())
|
||||
}
|
||||
notAfter := strconv.FormatFloat(float64(cert.NotAfter.UnixNano()/1e9), 'g', -1, 64)
|
||||
if ok := strings.Contains(rr.Body.String(), "ssl_cert_not_after{cn=\"example.ribbybibby.me\",dnsnames=\",example.ribbybibby.me,example-2.ribbybibby.me,example-3.ribbybibby.me,\",emails=\",me@ribbybibby.me,example@ribbybibby.me,\",ips=\",127.0.0.1,::1,\",issuer_cn=\"example.ribbybibby.me\",ou=\",ribbybibbys org,\",serial_no=\"100\"} "+notAfter); !ok {
|
||||
t.Errorf("expected `ssl_cert_not_after{cn=\"example.ribbybibby.me\",dnsnames=\",example.ribbybibby.me,example-2.ribbybibby.me,example-3.ribbybibby.me,\",emails=\",me@ribbybibby.me,example@ribbybibby.me,\",ips=\",127.0.0.1,::1,\",issuer_cn=\"example.ribbybibby.me\",ou=\",ribbybibbys org,\",serial_no=\"100\"} " + notAfter + "`")
|
||||
}
|
||||
notBefore := strconv.FormatFloat(float64(cert.NotBefore.UnixNano()/1e9), 'g', -1, 64)
|
||||
if ok := strings.Contains(rr.Body.String(), "ssl_cert_not_before{cn=\"example.ribbybibby.me\",dnsnames=\",example.ribbybibby.me,example-2.ribbybibby.me,example-3.ribbybibby.me,\",emails=\",me@ribbybibby.me,example@ribbybibby.me,\",ips=\",127.0.0.1,::1,\",issuer_cn=\"example.ribbybibby.me\",ou=\",ribbybibbys org,\",serial_no=\"100\"} "+notBefore); !ok {
|
||||
t.Errorf("expected `ssl_cert_not_before{cn=\"example.ribbybibby.me\",dnsnames=\",example.ribbybibby.me,example-2.ribbybibby.me,example-3.ribbybibby.me,\",emails=\",me@ribbybibby.me,example@ribbybibby.me,\",ips=\",127.0.0.1,::1,\",issuer_cn=\"example.ribbybibby.me\",ou=\",ribbybibbys org,\",serial_no=\"100\"} " + notBefore + "`")
|
||||
}
|
||||
|
||||
// Check TLS version metric
|
||||
ok := strings.Contains(rr.Body.String(), "ssl_tls_version_info{version=\"TLS 1.3\"} 1")
|
||||
@ -237,19 +227,9 @@ func TestProbeHandlerTCP(t *testing.T) {
|
||||
}
|
||||
|
||||
// Check notAfter and notBefore metrics
|
||||
block, _ := pem.Decode(certPEM)
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
if err := checkDates(certPEM, rr.Body.String()); err != nil {
|
||||
t.Errorf(err.Error())
|
||||
}
|
||||
notAfter := strconv.FormatFloat(float64(cert.NotAfter.UnixNano()/1e9), 'g', -1, 64)
|
||||
if ok := strings.Contains(rr.Body.String(), "ssl_cert_not_after{cn=\"example.ribbybibby.me\",dnsnames=\",example.ribbybibby.me,example-2.ribbybibby.me,example-3.ribbybibby.me,\",emails=\",me@ribbybibby.me,example@ribbybibby.me,\",ips=\",127.0.0.1,::1,\",issuer_cn=\"example.ribbybibby.me\",ou=\",ribbybibbys org,\",serial_no=\"100\"} "+notAfter); !ok {
|
||||
t.Errorf("expected `ssl_cert_not_after{cn=\"example.ribbybibby.me\",dnsnames=\",example.ribbybibby.me,example-2.ribbybibby.me,example-3.ribbybibby.me,\",emails=\",me@ribbybibby.me,example@ribbybibby.me,\",ips=\",127.0.0.1,::1,\",issuer_cn=\"example.ribbybibby.me\",ou=\",ribbybibbys org,\",serial_no=\"100\"} " + notAfter + "`")
|
||||
}
|
||||
notBefore := strconv.FormatFloat(float64(cert.NotBefore.UnixNano()/1e9), 'g', -1, 64)
|
||||
if ok := strings.Contains(rr.Body.String(), "ssl_cert_not_before{cn=\"example.ribbybibby.me\",dnsnames=\",example.ribbybibby.me,example-2.ribbybibby.me,example-3.ribbybibby.me,\",emails=\",me@ribbybibby.me,example@ribbybibby.me,\",ips=\",127.0.0.1,::1,\",issuer_cn=\"example.ribbybibby.me\",ou=\",ribbybibbys org,\",serial_no=\"100\"} "+notBefore); !ok {
|
||||
t.Errorf("expected `ssl_cert_not_before{cn=\"example.ribbybibby.me\",dnsnames=\",example.ribbybibby.me,example-2.ribbybibby.me,example-3.ribbybibby.me,\",emails=\",me@ribbybibby.me,example@ribbybibby.me,\",ips=\",127.0.0.1,::1,\",issuer_cn=\"example.ribbybibby.me\",ou=\",ribbybibbys org,\",serial_no=\"100\"} " + notBefore + "`")
|
||||
}
|
||||
}
|
||||
|
||||
// TestProbeHandlerTCPNoServer tests against a tcp server that doesn't exist
|
||||
@ -490,6 +470,162 @@ func TestProbeHandlerProxy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestProbeHandlerTCPStartTLSSMTP tests STARTTLS with a smtp server
|
||||
func TestProbeHandlerTCPStartTLSSMTP(t *testing.T) {
|
||||
server, certPEM, _, caFile, teardown, err := test.SetupTCPServer()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
defer teardown()
|
||||
|
||||
server.StartSMTP()
|
||||
defer server.Close()
|
||||
|
||||
conf := &config.Config{
|
||||
Modules: map[string]config.Module{
|
||||
"smtp": config.Module{
|
||||
Prober: "tcp",
|
||||
TLSConfig: pconfig.TLSConfig{
|
||||
CAFile: caFile,
|
||||
},
|
||||
TCP: config.TCPProbe{
|
||||
StartTLS: "smtp",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rr, err := probe(server.Listener.Addr().String(), "smtp", conf)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
// Check success metric
|
||||
if ok := strings.Contains(rr.Body.String(), "ssl_tls_connect_success 1"); !ok {
|
||||
t.Errorf("expected `ssl_tls_connect_success 1`")
|
||||
}
|
||||
|
||||
// Check probe metric
|
||||
if ok := strings.Contains(rr.Body.String(), "ssl_prober{prober=\"tcp\"} 1"); !ok {
|
||||
t.Errorf("expected `ssl_prober{prober=\"tcp\"} 1`")
|
||||
}
|
||||
|
||||
// Check notAfter and notBefore metrics
|
||||
if err := checkDates(certPEM, rr.Body.String()); err != nil {
|
||||
t.Errorf(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// TestProbeHandlerTCPStartTLSFTP tests STARTTLS with a ftp server
|
||||
func TestProbeHandlerTCPStartTLSFTP(t *testing.T) {
|
||||
server, certPEM, _, caFile, teardown, err := test.SetupTCPServer()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
defer teardown()
|
||||
|
||||
server.StartFTP()
|
||||
defer server.Close()
|
||||
|
||||
conf := &config.Config{
|
||||
Modules: map[string]config.Module{
|
||||
"ftp": config.Module{
|
||||
Prober: "tcp",
|
||||
TLSConfig: pconfig.TLSConfig{
|
||||
CAFile: caFile,
|
||||
},
|
||||
TCP: config.TCPProbe{
|
||||
StartTLS: "ftp",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rr, err := probe(server.Listener.Addr().String(), "ftp", conf)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
// Check success metric
|
||||
if ok := strings.Contains(rr.Body.String(), "ssl_tls_connect_success 1"); !ok {
|
||||
t.Errorf("expected `ssl_tls_connect_success 1`")
|
||||
}
|
||||
|
||||
// Check probe metric
|
||||
if ok := strings.Contains(rr.Body.String(), "ssl_prober{prober=\"tcp\"} 1"); !ok {
|
||||
t.Errorf("expected `ssl_prober{prober=\"tcp\"} 1`")
|
||||
}
|
||||
|
||||
// Check notAfter and notBefore metrics
|
||||
if err := checkDates(certPEM, rr.Body.String()); err != nil {
|
||||
t.Errorf(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// TestProbeHandlerTCPStartTLSIMAP tests STARTTLS with an imap server
|
||||
func TestProbeHandlerTCPStartTLSIMAP(t *testing.T) {
|
||||
server, certPEM, _, caFile, teardown, err := test.SetupTCPServer()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
defer teardown()
|
||||
|
||||
server.StartIMAP()
|
||||
defer server.Close()
|
||||
|
||||
conf := &config.Config{
|
||||
Modules: map[string]config.Module{
|
||||
"imap": config.Module{
|
||||
Prober: "tcp",
|
||||
TLSConfig: pconfig.TLSConfig{
|
||||
CAFile: caFile,
|
||||
},
|
||||
TCP: config.TCPProbe{
|
||||
StartTLS: "imap",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rr, err := probe(server.Listener.Addr().String(), "imap", conf)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
// Check success metric
|
||||
if ok := strings.Contains(rr.Body.String(), "ssl_tls_connect_success 1"); !ok {
|
||||
t.Errorf("expected `ssl_tls_connect_success 1`")
|
||||
}
|
||||
|
||||
// Check probe metric
|
||||
if ok := strings.Contains(rr.Body.String(), "ssl_prober{prober=\"tcp\"} 1"); !ok {
|
||||
t.Errorf("expected `ssl_prober{prober=\"tcp\"} 1`")
|
||||
}
|
||||
|
||||
// Check notAfter and notBefore metrics
|
||||
if err := checkDates(certPEM, rr.Body.String()); err != nil {
|
||||
t.Errorf(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func checkDates(certPEM []byte, body string) error {
|
||||
// Check notAfter and notBefore metrics
|
||||
block, _ := pem.Decode(certPEM)
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
notAfter := strconv.FormatFloat(float64(cert.NotAfter.UnixNano()/1e9), 'g', -1, 64)
|
||||
if ok := strings.Contains(body, "ssl_cert_not_after{cn=\"example.ribbybibby.me\",dnsnames=\",example.ribbybibby.me,example-2.ribbybibby.me,example-3.ribbybibby.me,\",emails=\",me@ribbybibby.me,example@ribbybibby.me,\",ips=\",127.0.0.1,::1,\",issuer_cn=\"example.ribbybibby.me\",ou=\",ribbybibbys org,\",serial_no=\"100\"} "+notAfter); !ok {
|
||||
return fmt.Errorf("expected `ssl_cert_not_after{cn=\"example.ribbybibby.me\",dnsnames=\",example.ribbybibby.me,example-2.ribbybibby.me,example-3.ribbybibby.me,\",emails=\",me@ribbybibby.me,example@ribbybibby.me,\",ips=\",127.0.0.1,::1,\",issuer_cn=\"example.ribbybibby.me\",ou=\",ribbybibbys org,\",serial_no=\"100\"} " + notAfter + "`")
|
||||
}
|
||||
notBefore := strconv.FormatFloat(float64(cert.NotBefore.UnixNano()/1e9), 'g', -1, 64)
|
||||
if ok := strings.Contains(body, "ssl_cert_not_before{cn=\"example.ribbybibby.me\",dnsnames=\",example.ribbybibby.me,example-2.ribbybibby.me,example-3.ribbybibby.me,\",emails=\",me@ribbybibby.me,example@ribbybibby.me,\",ips=\",127.0.0.1,::1,\",issuer_cn=\"example.ribbybibby.me\",ou=\",ribbybibbys org,\",serial_no=\"100\"} "+notBefore); !ok {
|
||||
return fmt.Errorf("expected `ssl_cert_not_before{cn=\"example.ribbybibby.me\",dnsnames=\",example.ribbybibby.me,example-2.ribbybibby.me,example-3.ribbybibby.me,\",emails=\",me@ribbybibby.me,example@ribbybibby.me,\",ips=\",127.0.0.1,::1,\",issuer_cn=\"example.ribbybibby.me\",ou=\",ribbybibbys org,\",serial_no=\"100\"} " + notBefore + "`")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func probe(target, module string, conf *config.Config) (*httptest.ResponseRecorder, error) {
|
||||
uri := "/probe?target=" + target
|
||||
if module != "" {
|
||||
|
99
test/tcp.go
99
test/tcp.go
@ -17,7 +17,7 @@ type TCPServer struct {
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
// StartTLS starts a listener that performs a TLS handshake
|
||||
// StartTLS starts a listener that performs an immediate TLS handshake
|
||||
func (t *TCPServer) StartTLS() {
|
||||
go func() {
|
||||
ln := tls.NewListener(t.Listener, t.TLS)
|
||||
@ -39,6 +39,103 @@ func (t *TCPServer) StartTLS() {
|
||||
}()
|
||||
}
|
||||
|
||||
// StartSMTP starts a listener that negotiates a TLS connection with an smtp
|
||||
// client using STARTTLS
|
||||
func (t *TCPServer) StartSMTP() {
|
||||
go func() {
|
||||
conn, err := t.Listener.Accept()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error accepting on socket: %s", err))
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
if err := conn.SetDeadline(time.Now().Add(5 * time.Second)); err != nil {
|
||||
panic("Error setting deadline")
|
||||
}
|
||||
|
||||
fmt.Fprintf(conn, "220 ESMTP StartTLS pseudo-server\n")
|
||||
if _, e := fmt.Fscanf(conn, "EHLO prober\n"); e != nil {
|
||||
panic("Error in dialog. No EHLO received.")
|
||||
}
|
||||
fmt.Fprintf(conn, "250-pseudo-server.example.net\n")
|
||||
fmt.Fprintf(conn, "250-STARTTLS\n")
|
||||
fmt.Fprintf(conn, "250 DSN\n")
|
||||
|
||||
if _, e := fmt.Fscanf(conn, "STARTTLS\n"); e != nil {
|
||||
panic("Error in dialog. No (TLS) STARTTLS received.")
|
||||
}
|
||||
fmt.Fprintf(conn, "220 2.0.0 Ready to start TLS\n")
|
||||
|
||||
// Upgrade to TLS.
|
||||
tlsConn := tls.Server(conn, t.TLS)
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
defer tlsConn.Close()
|
||||
|
||||
t.stopCh <- struct{}{}
|
||||
}()
|
||||
}
|
||||
|
||||
// StartFTP starts a listener that negotiates a TLS connection with an ftp
|
||||
// client using AUTH TLS
|
||||
func (t *TCPServer) StartFTP() {
|
||||
go func() {
|
||||
conn, err := t.Listener.Accept()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error accepting on socket: %s", err))
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
fmt.Fprintf(conn, "220 Test FTP Service\n")
|
||||
if _, e := fmt.Fscanf(conn, "AUTH TLS\n"); e != nil {
|
||||
panic("Error in dialog. No AUTH TLS received.")
|
||||
}
|
||||
fmt.Fprintf(conn, "234 AUTH command ok. Expecting TLS Negotiation.\n")
|
||||
|
||||
// Upgrade to TLS.
|
||||
tlsConn := tls.Server(conn, t.TLS)
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
defer tlsConn.Close()
|
||||
|
||||
t.stopCh <- struct{}{}
|
||||
}()
|
||||
}
|
||||
|
||||
// StartIMAP starts a listener that negotiates a TLS connection with an imap
|
||||
// client using STARTTLS
|
||||
func (t *TCPServer) StartIMAP() {
|
||||
go func() {
|
||||
conn, err := t.Listener.Accept()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error accepting on socket: %s", err))
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
fmt.Fprintf(conn, "* OK XIMAP ready for requests\n")
|
||||
if _, e := fmt.Fscanf(conn, ". CAPABILITY\n"); e != nil {
|
||||
panic("Error in dialog. No . CAPABILITY received.")
|
||||
}
|
||||
fmt.Fprintf(conn, "* CAPABILITY IMAP4 IMAP4rev1 AUTH=PLAIN STARTTLS\n")
|
||||
fmt.Fprintf(conn, ". OK CAPABILITY completed.\n")
|
||||
if _, e := fmt.Fscanf(conn, ". STARTTLS\n"); e != nil {
|
||||
panic("Error in dialog. No . STARTTLS received.")
|
||||
}
|
||||
fmt.Fprintf(conn, ". OK Begin TLS negotiation now.\n")
|
||||
|
||||
// Upgrade to TLS.
|
||||
tlsConn := tls.Server(conn, t.TLS)
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
defer tlsConn.Close()
|
||||
|
||||
t.stopCh <- struct{}{}
|
||||
}()
|
||||
}
|
||||
|
||||
// Close stops the server and closes the listener
|
||||
func (t *TCPServer) Close() {
|
||||
<-t.stopCh
|
||||
|
Loading…
Reference in New Issue
Block a user