master
go 131 lines 3.18 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package x509check
4
5 import (
6 "crypto/tls"
7 "crypto/x509"
8 "encoding/pem"
9 "fmt"
10 "net"
11 "net/smtp"
12 "net/url"
13 "os"
14 "time"
15
16 "github.com/netdata/netdata/go/plugins/pkg/tlscfg"
17 )
18
19 type provider interface {
20 certificates() ([]*x509.Certificate, error)
21 }
22
23 type fromFile struct {
24 path string
25 }
26
27 type fromNet struct {
28 url *url.URL
29 tlsConfig *tls.Config
30 timeout time.Duration
31 }
32
33 type fromSMTP struct {
34 url *url.URL
35 tlsConfig *tls.Config
36 timeout time.Duration
37 }
38
39 func newProvider(config Config) (provider, error) {
40 sourceURL, err := url.Parse(config.Source)
41 if err != nil {
42 return nil, fmt.Errorf("source parse: %v", err)
43 }
44
45 tlsCfg, err := tlscfg.NewTLSConfig(config.TLSConfig)
46 if err != nil {
47 return nil, fmt.Errorf("create tls config: %v", err)
48 }
49
50 if tlsCfg == nil {
51 tlsCfg = &tls.Config{}
52 }
53 tlsCfg.ServerName = sourceURL.Hostname()
54
55 switch sourceURL.Scheme {
56 case "file":
57 return &fromFile{path: sourceURL.Path}, nil
58 case "https", "udp", "udp4", "udp6", "tcp", "tcp4", "tcp6":
59 if sourceURL.Scheme == "https" {
60 sourceURL.Scheme = "tcp"
61 }
62 return &fromNet{url: sourceURL, tlsConfig: tlsCfg, timeout: config.Timeout.Duration()}, nil
63 case "smtp":
64 sourceURL.Scheme = "tcp"
65 return &fromSMTP{url: sourceURL, tlsConfig: tlsCfg, timeout: config.Timeout.Duration()}, nil
66 default:
67 return nil, fmt.Errorf("unsupported scheme '%s'", sourceURL)
68 }
69 }
70
71 func (f fromFile) certificates() ([]*x509.Certificate, error) {
72 content, err := os.ReadFile(f.path)
73 if err != nil {
74 return nil, fmt.Errorf("error on reading '%s': %v", f.path, err)
75 }
76
77 block, _ := pem.Decode(content)
78 if block == nil {
79 return nil, fmt.Errorf("error on decoding '%s': %v", f.path, err)
80 }
81
82 cert, err := x509.ParseCertificate(block.Bytes)
83 if err != nil {
84 return nil, fmt.Errorf("error on parsing certificate '%s': %v", f.path, err)
85 }
86
87 return []*x509.Certificate{cert}, nil
88 }
89
90 func (f fromNet) certificates() ([]*x509.Certificate, error) {
91 ipConn, err := net.DialTimeout(f.url.Scheme, f.url.Host, f.timeout)
92 if err != nil {
93 return nil, fmt.Errorf("error on dial to '%s': %v", f.url, err)
94 }
95 defer func() { _ = ipConn.Close() }()
96
97 conn := tls.Client(ipConn, f.tlsConfig.Clone())
98 defer func() { _ = conn.Close() }()
99 if err := conn.Handshake(); err != nil {
100 return nil, fmt.Errorf("error on SSL handshake with '%s': %v", f.url, err)
101 }
102
103 certs := conn.ConnectionState().PeerCertificates
104 return certs, nil
105 }
106
107 func (f fromSMTP) certificates() ([]*x509.Certificate, error) {
108 ipConn, err := net.DialTimeout(f.url.Scheme, f.url.Host, f.timeout)
109 if err != nil {
110 return nil, fmt.Errorf("error on dial to '%s': %v", f.url, err)
111 }
112 defer func() { _ = ipConn.Close() }()
113
114 host, _, _ := net.SplitHostPort(f.url.Host)
115 smtpClient, err := smtp.NewClient(ipConn, host)
116 if err != nil {
117 return nil, fmt.Errorf("error on creating SMTP client: %v", err)
118 }
119 defer func() { _ = smtpClient.Quit() }()
120
121 err = smtpClient.StartTLS(f.tlsConfig.Clone())
122 if err != nil {
123 return nil, fmt.Errorf("error on startTLS with '%s': %v", f.url, err)
124 }
125
126 conn, ok := smtpClient.TLSConnectionState()
127 if !ok {
128 return nil, fmt.Errorf("startTLS didn't succeed")
129 }
130 return conn.PeerCertificates, nil
131 }