sdk.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. /*
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package common
  13. import (
  14. "context"
  15. "crypto/tls"
  16. "crypto/x509"
  17. "errors"
  18. "time"
  19. "github.com/yandex-cloud/go-genproto/yandex/cloud/endpoint"
  20. ycsdk "github.com/yandex-cloud/go-sdk"
  21. "github.com/yandex-cloud/go-sdk/iamkey"
  22. "google.golang.org/grpc"
  23. "google.golang.org/grpc/credentials"
  24. "google.golang.org/grpc/keepalive"
  25. )
  26. // Creates a connection to the given Yandex.Cloud API endpoint.
  27. func NewGrpcConnection(
  28. ctx context.Context,
  29. apiEndpoint string,
  30. apiEndpointID string, // an ID from https://api.cloud.yandex.net/endpoints
  31. authorizedKey *iamkey.Key,
  32. caCertificate []byte,
  33. ) (*grpc.ClientConn, error) {
  34. tlsConfig, err := tlsConfig(caCertificate)
  35. if err != nil {
  36. return nil, err
  37. }
  38. sdk, err := buildSDK(ctx, apiEndpoint, authorizedKey, tlsConfig)
  39. if err != nil {
  40. return nil, err
  41. }
  42. defer func() {
  43. _ = closeSDK(ctx, sdk)
  44. }()
  45. serviceAPIEndpoint, err := sdk.ApiEndpoint().ApiEndpoint().Get(ctx, &endpoint.GetApiEndpointRequest{
  46. ApiEndpointId: apiEndpointID,
  47. })
  48. if err != nil {
  49. return nil, err
  50. }
  51. // Until gRPC proposal A61 is implemented in grpc-go, default gRPC name resolver (dns)
  52. // is incompatible with dualstack backends, and YC API backends are dualstack.
  53. // However, if passthrough resolver is used instead, grpc-go won't do any name resolution
  54. // and will pass the endpoint to net.Dial as-is, which would utilize happy-eyeballs
  55. // support in Go's net package.
  56. // So we explicitly set gRPC resolver to `passthrough` to match `ycsdk`s behavior,
  57. // which uses `passthrough` resolver implicitly by using deprecated grpc.DialContext
  58. // instead of grpc.NewClient used here
  59. target := "passthrough:///" + serviceAPIEndpoint.Address
  60. return grpc.NewClient(target,
  61. grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
  62. grpc.WithKeepaliveParams(keepalive.ClientParameters{
  63. Time: time.Second * 30,
  64. Timeout: time.Second * 10,
  65. PermitWithoutStream: false,
  66. }),
  67. grpc.WithUserAgent("external-secrets"),
  68. )
  69. }
  70. // Exchanges the given authorized key to an IAM token.
  71. func NewIamToken(ctx context.Context, apiEndpoint string, authorizedKey *iamkey.Key, caCertificate []byte) (*IamToken, error) {
  72. tlsConfig, err := tlsConfig(caCertificate)
  73. if err != nil {
  74. return nil, err
  75. }
  76. sdk, err := buildSDK(ctx, apiEndpoint, authorizedKey, tlsConfig)
  77. if err != nil {
  78. return nil, err
  79. }
  80. defer func() {
  81. _ = closeSDK(ctx, sdk)
  82. }()
  83. iamToken, err := sdk.CreateIAMToken(ctx)
  84. if err != nil {
  85. return nil, err
  86. }
  87. return &IamToken{Token: iamToken.IamToken, ExpiresAt: iamToken.ExpiresAt.AsTime()}, nil
  88. }
  89. func tlsConfig(caCertificate []byte) (*tls.Config, error) {
  90. tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12}
  91. if caCertificate != nil {
  92. caCertPool := x509.NewCertPool()
  93. ok := caCertPool.AppendCertsFromPEM(caCertificate)
  94. if !ok {
  95. return nil, errors.New("unable to read trusted CA certificates")
  96. }
  97. tlsConfig.RootCAs = caCertPool
  98. }
  99. return tlsConfig, nil
  100. }
  101. func buildSDK(ctx context.Context, apiEndpoint string, authorizedKey *iamkey.Key, tlsConfig *tls.Config) (*ycsdk.SDK, error) {
  102. creds, err := ycsdk.ServiceAccountKey(authorizedKey)
  103. if err != nil {
  104. return nil, err
  105. }
  106. sdk, err := ycsdk.Build(ctx, ycsdk.Config{
  107. Credentials: creds,
  108. Endpoint: apiEndpoint,
  109. TLSConfig: tlsConfig,
  110. })
  111. if err != nil {
  112. return nil, err
  113. }
  114. return sdk, nil
  115. }
  116. func closeSDK(ctx context.Context, sdk *ycsdk.SDK) error {
  117. return sdk.Shutdown(ctx)
  118. }
  119. type PerRPCCredentials struct {
  120. IamToken string
  121. }
  122. func (t PerRPCCredentials) GetRequestMetadata(_ context.Context, _ ...string) (map[string]string, error) {
  123. return map[string]string{"Authorization": "Bearer " + t.IamToken}, nil
  124. }
  125. func (PerRPCCredentials) RequireTransportSecurity() bool {
  126. return true
  127. }