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 }