github.com/Jeffail/benthos/v3@v3.65.0/lib/util/kafka/sasl/sasl.go (about)

     1  package sasl
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/Jeffail/benthos/v3/internal/docs"
     9  	"github.com/Jeffail/benthos/v3/internal/interop"
    10  	"github.com/Jeffail/benthos/v3/lib/types"
    11  	"github.com/Shopify/sarama"
    12  )
    13  
    14  // SASL specific error types.
    15  var (
    16  	ErrUnsupportedSASLMechanism = errors.New("unsupported SASL mechanism")
    17  )
    18  
    19  // Config contains configuration for SASL based authentication.
    20  // TODO: V4 Remove "enabled" and set a default mechanism
    21  type Config struct {
    22  	Enabled     bool   `json:"enabled" yaml:"enabled"` // DEPRECATED
    23  	Mechanism   string `json:"mechanism" yaml:"mechanism"`
    24  	User        string `json:"user" yaml:"user"`
    25  	Password    string `json:"password" yaml:"password"`
    26  	AccessToken string `json:"access_token" yaml:"access_token"`
    27  	TokenCache  string `json:"token_cache" yaml:"token_cache"`
    28  	TokenKey    string `json:"token_key" yaml:"token_key"`
    29  }
    30  
    31  // NewConfig returns a new SASL config for Kafka with default values.
    32  func NewConfig() Config {
    33  	return Config{} // Well, that seemed pointless.
    34  }
    35  
    36  // FieldSpec returns specs for SASL fields.
    37  func FieldSpec() docs.FieldSpec {
    38  	return docs.FieldAdvanced("sasl", "Enables SASL authentication.").WithChildren(
    39  		docs.FieldDeprecated("enabled"),
    40  		docs.FieldCommon("mechanism", "The SASL authentication mechanism, if left empty SASL authentication is not used. Warning: SCRAM based methods within Benthos have not received a security audit.").HasAnnotatedOptions(
    41  			sarama.SASLTypePlaintext, "Plain text authentication. NOTE: When using plain text auth it is extremely likely that you'll also need to [enable TLS](#tlsenabled).",
    42  			sarama.SASLTypeOAuth, "OAuth Bearer based authentication.",
    43  			sarama.SASLTypeSCRAMSHA256, "Authentication using the SCRAM-SHA-256 mechanism.",
    44  			sarama.SASLTypeSCRAMSHA512, "Authentication using the SCRAM-SHA-512 mechanism.",
    45  		),
    46  		docs.FieldCommon("user", "A `"+sarama.SASLTypePlaintext+"` username. It is recommended that you use environment variables to populate this field.", "${USER}"),
    47  		docs.FieldCommon("password", "A `"+sarama.SASLTypePlaintext+"` password. It is recommended that you use environment variables to populate this field.", "${PASSWORD}"),
    48  		docs.FieldAdvanced("access_token", "A static `"+sarama.SASLTypeOAuth+"` access token"),
    49  		docs.FieldAdvanced("token_cache", "Instead of using a static `access_token` allows you to query a [`cache`](/docs/components/caches/about) resource to fetch `"+sarama.SASLTypeOAuth+"` tokens from"),
    50  		docs.FieldAdvanced("token_key", "Required when using a `token_cache`, the key to query the cache with for tokens."),
    51  	)
    52  }
    53  
    54  // Apply applies the SASL authentication configuration to a Sarama config object.
    55  func (s Config) Apply(mgr types.Manager, conf *sarama.Config) error {
    56  	if s.Enabled && s.Mechanism == "" {
    57  		s.Mechanism = sarama.SASLTypePlaintext
    58  	}
    59  	switch s.Mechanism {
    60  	case sarama.SASLTypeOAuth:
    61  		var tp sarama.AccessTokenProvider
    62  		var err error
    63  
    64  		if s.TokenCache != "" {
    65  			tp, err = newCacheAccessTokenProvider(mgr, s.TokenCache, s.TokenKey)
    66  			if err != nil {
    67  				return err
    68  			}
    69  		} else {
    70  			tp, err = newStaticAccessTokenProvider(s.AccessToken)
    71  			if err != nil {
    72  				return err
    73  			}
    74  		}
    75  		conf.Net.SASL.TokenProvider = tp
    76  	case sarama.SASLTypeSCRAMSHA256:
    77  		conf.Net.SASL.SCRAMClientGeneratorFunc = func() sarama.SCRAMClient {
    78  			return &XDGSCRAMClient{HashGeneratorFcn: SHA256}
    79  		}
    80  		conf.Net.SASL.User = s.User
    81  		conf.Net.SASL.Password = s.Password
    82  	case sarama.SASLTypeSCRAMSHA512:
    83  		conf.Net.SASL.SCRAMClientGeneratorFunc = func() sarama.SCRAMClient {
    84  			return &XDGSCRAMClient{HashGeneratorFcn: SHA512}
    85  		}
    86  		conf.Net.SASL.User = s.User
    87  		conf.Net.SASL.Password = s.Password
    88  	case sarama.SASLTypePlaintext:
    89  		conf.Net.SASL.User = s.User
    90  		conf.Net.SASL.Password = s.Password
    91  	case "":
    92  		return nil
    93  	default:
    94  		return ErrUnsupportedSASLMechanism
    95  	}
    96  
    97  	conf.Net.SASL.Enable = true
    98  	conf.Net.SASL.Mechanism = sarama.SASLMechanism(s.Mechanism)
    99  
   100  	return nil
   101  }
   102  
   103  //------------------------------------------------------------------------------
   104  
   105  // cacheAccessTokenProvider fetches SASL OAUTHBEARER access tokens from a cache.
   106  type cacheAccessTokenProvider struct {
   107  	mgr       types.Manager
   108  	cacheName string
   109  	key       string
   110  }
   111  
   112  func newCacheAccessTokenProvider(mgr types.Manager, cache, key string) (*cacheAccessTokenProvider, error) {
   113  	if err := interop.ProbeCache(context.Background(), mgr, cache); err != nil {
   114  		return nil, err
   115  	}
   116  	return &cacheAccessTokenProvider{
   117  		mgr:       mgr,
   118  		cacheName: cache,
   119  		key:       key,
   120  	}, nil
   121  }
   122  
   123  func (c *cacheAccessTokenProvider) Token() (*sarama.AccessToken, error) {
   124  	var tok []byte
   125  	var terr error
   126  	if err := interop.AccessCache(context.Background(), c.mgr, c.cacheName, func(cache types.Cache) {
   127  		tok, terr = cache.Get(c.key)
   128  	}); err != nil {
   129  		return nil, fmt.Errorf("failed to obtain cache resource '%v': %v", c.cacheName, err)
   130  	}
   131  	if terr != nil {
   132  		return nil, terr
   133  	}
   134  	return &sarama.AccessToken{Token: string(tok)}, nil
   135  }
   136  
   137  //------------------------------------------------------------------------------
   138  
   139  // staticAccessTokenProvider provides a static SASL OAUTHBEARER access token.
   140  type staticAccessTokenProvider struct {
   141  	token string
   142  }
   143  
   144  func newStaticAccessTokenProvider(token string) (*staticAccessTokenProvider, error) {
   145  	return &staticAccessTokenProvider{token}, nil
   146  }
   147  
   148  func (s *staticAccessTokenProvider) Token() (*sarama.AccessToken, error) {
   149  	return &sarama.AccessToken{Token: s.token}, nil
   150  }
   151  
   152  //------------------------------------------------------------------------------