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 //------------------------------------------------------------------------------