github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/tlscommon/tls_config.go (about)

     1  // Copyright 2023 iLogtail Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package tlscommon
    16  
    17  import (
    18  	"crypto/tls"
    19  	"crypto/x509"
    20  	"errors"
    21  	"fmt"
    22  	"os"
    23  	"path/filepath"
    24  )
    25  
    26  // The defaults should be a safe configuration
    27  const defaultMinTLSVersion = tls.VersionTLS12
    28  
    29  // Uses the default MaxVersion from "crypto/tls"
    30  const defaultMaxTLSVersion = 0
    31  
    32  // TLSConfig is the interface used to configure a tcp client or server from a `Config`
    33  type TLSConfig struct {
    34  	// Enable TLS
    35  	Enabled bool
    36  	// Path to the CA cert. For a client this verifies the server certificate.
    37  	CAFile string
    38  	// Path to the TLS cert to use for TLS required connections. (optional)
    39  	CertFile string
    40  	// Path to the TLS key to use for TLS required connections. (optional)
    41  	KeyFile string
    42  	// InsecureSkipVerify will enable TLS but not verify the certificate
    43  	InsecureSkipVerify bool
    44  	// MinVersion sets the minimum TLS version that is acceptable.
    45  	// If not set, TLS 1.2 will be used. (optional)
    46  	MinVersion string
    47  	// MaxVersion sets the maximum TLS version that is acceptable.
    48  	// If not set, refer to crypto/tls for defaults. (optional)
    49  	MaxVersion string
    50  }
    51  
    52  func (c *TLSConfig) LoadTLSConfig() (*tls.Config, error) {
    53  	if !c.Enabled {
    54  		return nil, nil
    55  	}
    56  	var err error
    57  	var certPool *x509.CertPool
    58  	if c.CAFile != "" {
    59  		certPool, err = c.loadCert(c.CAFile)
    60  		if err != nil {
    61  			return nil, fmt.Errorf("failed to load CA CertPool: %w", err)
    62  		}
    63  	}
    64  	if (c.CertFile == "" && c.KeyFile != "") || (c.CertFile != "" && c.KeyFile == "") {
    65  		return nil, errors.New("for auth via TLS, either both certificate and key must be supplied, or neither")
    66  	}
    67  	var cert tls.Certificate
    68  	if c.CertFile != "" && c.KeyFile != "" {
    69  		cert, err = tls.LoadX509KeyPair(c.CertFile, c.KeyFile)
    70  		if err != nil {
    71  			return nil, fmt.Errorf("could not load TLS client key/certificate from %s:%s: %s", c.KeyFile, c.CertFile, err)
    72  		}
    73  	}
    74  
    75  	minVersion, err := convertVersion(c.MinVersion, defaultMinTLSVersion)
    76  	if err != nil {
    77  		return nil, fmt.Errorf("invalid TLS min_version: %w", err)
    78  	}
    79  	maxVersion, err := convertVersion(c.MaxVersion, defaultMaxTLSVersion)
    80  	if err != nil {
    81  		return nil, fmt.Errorf("invalid TLS max_version: %w", err)
    82  	}
    83  	return &tls.Config{
    84  		RootCAs:            certPool,
    85  		Certificates:       []tls.Certificate{cert},
    86  		InsecureSkipVerify: c.InsecureSkipVerify, //nolint:gosec
    87  		MinVersion:         minVersion,
    88  		MaxVersion:         maxVersion,
    89  	}, nil
    90  }
    91  
    92  func (c *TLSConfig) loadCert(caPath string) (*x509.CertPool, error) {
    93  	caPEM, err := os.ReadFile(filepath.Clean(caPath))
    94  	if err != nil {
    95  		return nil, fmt.Errorf("failed to load CA %s: %w", caPath, err)
    96  	}
    97  	certPool := x509.NewCertPool()
    98  	if !certPool.AppendCertsFromPEM(caPEM) {
    99  		return nil, fmt.Errorf("failed to parse CA %s", caPath)
   100  	}
   101  	return certPool, nil
   102  }
   103  
   104  func convertVersion(version string, defaultVersion uint16) (uint16, error) {
   105  	if version == "" {
   106  		return defaultVersion, nil
   107  	}
   108  	val, ok := tlsProtocolVersions[version]
   109  	if !ok {
   110  		return 0, fmt.Errorf("unsupported TLS version: %q", version)
   111  	}
   112  	return val, nil
   113  }
   114  
   115  var tlsProtocolVersions = map[string]uint16{
   116  	"1.0": tls.VersionTLS10,
   117  	"1.1": tls.VersionTLS11,
   118  	"1.2": tls.VersionTLS12,
   119  	"1.3": tls.VersionTLS13,
   120  }