storageauthorizer.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. package azure
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "time"
  7. "github.com/Azure/azure-sdk-for-go/sdk/azcore"
  8. "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
  9. "github.com/opencost/opencost/core/pkg/storage"
  10. "github.com/opencost/opencost/pkg/cloud"
  11. )
  12. const SharedKeyAuthorizerType = "AzureAccessKey"
  13. const StorageConnectionStringAuthorizerType = "AzureStorageConnectionString"
  14. var defaultHTTPConfig = storage.HTTPConfig{
  15. IdleConnTimeout: 90 * time.Second,
  16. ResponseHeaderTimeout: 2 * time.Minute,
  17. TLSHandshakeTimeout: 10 * time.Second,
  18. ExpectContinueTimeout: 1 * time.Second,
  19. MaxIdleConns: 100,
  20. MaxIdleConnsPerHost: 100,
  21. MaxConnsPerHost: 0,
  22. DisableCompression: false,
  23. }
  24. // StorageAuthorizer is a service specific Authorizer for Azure Storage, it exists so that we can support existing Shared
  25. // Key configurations while allowing the Authorizer to have a service agnostic api
  26. type StorageAuthorizer interface {
  27. cloud.Authorizer
  28. GetBlobClient(serviceURL string) (*azblob.Client, error)
  29. }
  30. // SelectStorageAuthorizerByType is an implementation of AuthorizerSelectorFn and acts as a register for Authorizer types
  31. func SelectStorageAuthorizerByType(typeStr string) (StorageAuthorizer, error) {
  32. switch typeStr {
  33. case SharedKeyAuthorizerType:
  34. return &SharedKeyCredential{}, nil
  35. case StorageConnectionStringAuthorizerType:
  36. return &StorageConnectionStringCredential{
  37. HTTPConfig: defaultHTTPConfig,
  38. }, nil
  39. default:
  40. authorizer, err := SelectAuthorizerByType(typeStr)
  41. if err != nil {
  42. return nil, err
  43. }
  44. return &AuthorizerHolder{authorizer}, nil
  45. }
  46. }
  47. // SharedKeyCredential is a StorageAuthorizer with credentials which cannot be used to authorize other services. This
  48. // is a legacy auth method which is not included in azidentity
  49. type SharedKeyCredential struct {
  50. AccessKey string `json:"accessKey"`
  51. Account string `json:"account"`
  52. }
  53. func (skc *SharedKeyCredential) MarshalJSON() ([]byte, error) {
  54. fmap := make(map[string]any, 3)
  55. fmap[cloud.AuthorizerTypeProperty] = SharedKeyAuthorizerType
  56. fmap["accessKey"] = skc.AccessKey
  57. fmap["account"] = skc.Account
  58. return json.Marshal(fmap)
  59. }
  60. func (skc *SharedKeyCredential) Validate() error {
  61. if skc.AccessKey == "" {
  62. return fmt.Errorf("SharedKeyCredential: missing access key")
  63. }
  64. if skc.Account == "" {
  65. return fmt.Errorf("SharedKeyCredential: missing account")
  66. }
  67. return nil
  68. }
  69. func (skc *SharedKeyCredential) Equals(config cloud.Config) bool {
  70. if config == nil {
  71. return false
  72. }
  73. thatConfig, ok := config.(*SharedKeyCredential)
  74. if !ok {
  75. return false
  76. }
  77. if skc.AccessKey != thatConfig.AccessKey {
  78. return false
  79. }
  80. if skc.Account != thatConfig.Account {
  81. return false
  82. }
  83. return true
  84. }
  85. func (skc *SharedKeyCredential) Sanitize() cloud.Config {
  86. return &SharedKeyCredential{
  87. AccessKey: cloud.Redacted,
  88. Account: skc.Account,
  89. }
  90. }
  91. func (skc *SharedKeyCredential) GetBlobClient(serviceURL string) (*azblob.Client, error) {
  92. credential, err := azblob.NewSharedKeyCredential(skc.Account, skc.AccessKey)
  93. if err != nil {
  94. return nil, err
  95. }
  96. client, err := azblob.NewClientWithSharedKeyCredential(serviceURL, credential, nil)
  97. return client, err
  98. }
  99. // AuthorizerHolder is a StorageAuthorizer implementation that wraps an Authorizer implementation
  100. type AuthorizerHolder struct {
  101. Authorizer
  102. }
  103. func (ah *AuthorizerHolder) Equals(config cloud.Config) bool {
  104. if config == nil {
  105. return false
  106. }
  107. that, ok := config.(*AuthorizerHolder)
  108. if !ok {
  109. return false
  110. }
  111. return ah.Authorizer.Equals(that.Authorizer)
  112. }
  113. func (ah *AuthorizerHolder) Sanitize() cloud.Config {
  114. return &AuthorizerHolder{Authorizer: ah.Authorizer.Sanitize().(Authorizer)}
  115. }
  116. func (ah *AuthorizerHolder) GetBlobClient(serviceURL string) (*azblob.Client, error) {
  117. // Create a default request pipeline using your storage account name and account key.
  118. cred, err := ah.GetCredential()
  119. if err != nil {
  120. return nil, fmt.Errorf("error retrieving credentials: %w", err)
  121. }
  122. client, err := azblob.NewClient(serviceURL, cred, nil)
  123. return client, err
  124. }
  125. // UnmarshalJSON passes the contained Authorizer to be unmarshalled into
  126. func (ah *AuthorizerHolder) UnmarshalJSON(b []byte) error {
  127. return json.Unmarshal(b, ah.Authorizer)
  128. }
  129. type StorageConnectionStringCredential struct {
  130. StorageConnectionString string `json:"storageConnectionString"`
  131. HTTPConfig storage.HTTPConfig `json:"httpConfig"`
  132. }
  133. func (s *StorageConnectionStringCredential) MarshalJSON() ([]byte, error) {
  134. fmap := make(map[string]any, 3)
  135. fmap[cloud.AuthorizerTypeProperty] = StorageConnectionStringAuthorizerType
  136. fmap["storageConnectionString"] = s.StorageConnectionString
  137. fmap["httpConfig"] = s.HTTPConfig
  138. return json.Marshal(fmap)
  139. }
  140. func (s *StorageConnectionStringCredential) Validate() error {
  141. if s.StorageConnectionString == "" {
  142. return fmt.Errorf("StorageConnectionStringCredential: missing storage connection string")
  143. }
  144. return nil
  145. }
  146. func (s *StorageConnectionStringCredential) Equals(config cloud.Config) bool {
  147. if config == nil {
  148. return false
  149. }
  150. thatConfig, ok := config.(*StorageConnectionStringCredential)
  151. if !ok {
  152. return false
  153. }
  154. if s.HTTPConfig != thatConfig.HTTPConfig {
  155. return false
  156. }
  157. if s.StorageConnectionString != thatConfig.StorageConnectionString {
  158. return false
  159. }
  160. return true
  161. }
  162. func (s *StorageConnectionStringCredential) Sanitize() cloud.Config {
  163. return &StorageConnectionStringCredential{
  164. StorageConnectionString: cloud.Redacted,
  165. HTTPConfig: s.HTTPConfig,
  166. }
  167. }
  168. func (s *StorageConnectionStringCredential) GetBlobClient(serviceURL string) (*azblob.Client, error) {
  169. dt, err := s.HTTPConfig.GetHTTPTransport()
  170. if err != nil {
  171. return nil, fmt.Errorf("error creating transport: %w", err)
  172. }
  173. options := &azblob.ClientOptions{
  174. ClientOptions: azcore.ClientOptions{
  175. Transport: &http.Client{Transport: dt},
  176. },
  177. }
  178. client, err := azblob.NewClientFromConnectionString(s.StorageConnectionString, options)
  179. return client, err
  180. }