github.com/cs3org/reva/v2@v2.27.7/pkg/events/stream/nats.go (about)

     1  package stream
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"errors"
     8  	"io"
     9  	"os"
    10  	"time"
    11  
    12  	"github.com/cenkalti/backoff"
    13  	"github.com/cs3org/reva/v2/pkg/events"
    14  	"github.com/cs3org/reva/v2/pkg/logger"
    15  	"github.com/go-micro/plugins/v4/events/natsjs"
    16  )
    17  
    18  // NatsConfig is the configuration needed for a NATS event stream
    19  type NatsConfig struct {
    20  	Endpoint             string `mapstructure:"address"`          // Endpoint of the nats server
    21  	Cluster              string `mapstructure:"clusterID"`        // CluserID of the nats cluster
    22  	TLSInsecure          bool   `mapstructure:"tls-insecure"`     // Whether to verify TLS certificates
    23  	TLSRootCACertificate string `mapstructure:"tls-root-ca-cert"` // The root CA certificate used to validate the TLS certificate
    24  	EnableTLS            bool   `mapstructure:"enable-tls"`       // Enable TLS
    25  	AuthUsername         string `mapstructure:"username"`         // Username for authentication
    26  	AuthPassword         string `mapstructure:"password"`         // Password for authentication
    27  
    28  }
    29  
    30  // NatsFromConfig returns a nats stream from the given config
    31  func NatsFromConfig(connName string, disableDurability bool, cfg NatsConfig) (events.Stream, error) {
    32  	var tlsConf *tls.Config
    33  	if cfg.EnableTLS {
    34  		var rootCAPool *x509.CertPool
    35  		if cfg.TLSRootCACertificate != "" {
    36  			rootCrtFile, err := os.Open(cfg.TLSRootCACertificate)
    37  			if err != nil {
    38  				return nil, err
    39  			}
    40  
    41  			rootCAPool, err = newCertPoolFromPEM(rootCrtFile)
    42  			if err != nil {
    43  				return nil, err
    44  			}
    45  			cfg.TLSInsecure = false
    46  		}
    47  
    48  		tlsConf = &tls.Config{
    49  			MinVersion:         tls.VersionTLS12,
    50  			InsecureSkipVerify: cfg.TLSInsecure, //nolint:gosec
    51  			RootCAs:            rootCAPool,
    52  		}
    53  	}
    54  
    55  	opts := []natsjs.Option{
    56  		natsjs.TLSConfig(tlsConf),
    57  		natsjs.Address(cfg.Endpoint),
    58  		natsjs.ClusterID(cfg.Cluster),
    59  		natsjs.SynchronousPublish(true),
    60  		natsjs.Name(connName),
    61  		natsjs.Authenticate(cfg.AuthUsername, cfg.AuthPassword),
    62  	}
    63  
    64  	if disableDurability {
    65  		opts = append(opts, natsjs.DisableDurableStreams())
    66  	}
    67  
    68  	return Nats(opts...)
    69  }
    70  
    71  // nats returns a nats streaming client
    72  // retries exponentially to connect to a nats server
    73  func Nats(opts ...natsjs.Option) (events.Stream, error) {
    74  	b := backoff.NewExponentialBackOff()
    75  	var stream events.Stream
    76  	o := func() error {
    77  		n := b.NextBackOff()
    78  		s, err := natsjs.NewStream(opts...)
    79  		if err != nil && n > time.Second {
    80  			logger.New().Error().Err(err).Msgf("can't connect to nats (jetstream) server, retrying in %s", n)
    81  		}
    82  		stream = s
    83  		return err
    84  	}
    85  
    86  	err := backoff.Retry(o, b)
    87  	return stream, err
    88  }
    89  
    90  // newCertPoolFromPEM reads certificates from io.Reader and returns a x509.CertPool
    91  // containing those certificates.
    92  func newCertPoolFromPEM(crts ...io.Reader) (*x509.CertPool, error) {
    93  	certPool := x509.NewCertPool()
    94  
    95  	var buf bytes.Buffer
    96  	for _, c := range crts {
    97  		if _, err := io.Copy(&buf, c); err != nil {
    98  			return nil, err
    99  		}
   100  		if !certPool.AppendCertsFromPEM(buf.Bytes()) {
   101  			return nil, errors.New("failed to append cert from PEM")
   102  		}
   103  		buf.Reset()
   104  	}
   105  
   106  	return certPool, nil
   107  }