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  }