github.com/netdata/go.d.plugin@v0.58.1/modules/x509check/provider.go (about) 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/go.d.plugin/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 }