-
Notifications
You must be signed in to change notification settings - Fork 149
/
sts.go
172 lines (152 loc) · 5.06 KB
/
sts.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package credential
import (
"context"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/json"
"fmt"
"os"
"time"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/util/retry"
"github.com/AliyunContainerService/terway/pkg/backoff"
"github.com/AliyunContainerService/terway/pkg/utils"
"github.com/AliyunContainerService/terway/pkg/utils/k8sclient"
)
type EncryptedCredentialInfo struct {
AccessKeyID string `json:"access.key.id"`
AccessKeySecret string `json:"access.key.secret"`
SecurityToken string `json:"security.token"`
Expiration string `json:"expiration"`
Keyring string `json:"keyring"`
}
type EncryptedCredentialProvider struct {
credentialPath string
secretNamespace string
secretName string
}
// NewEncryptedCredentialProvider get token from file or secret. default filepath /var/addon/token-config
func NewEncryptedCredentialProvider(credentialPath, secretNamespace, secretName string) *EncryptedCredentialProvider {
return &EncryptedCredentialProvider{credentialPath: credentialPath, secretNamespace: secretNamespace, secretName: secretName}
}
func (e *EncryptedCredentialProvider) Resolve() (*Credential, error) {
if e.credentialPath == "" && e.secretNamespace == "" && e.secretName == "" {
return nil, nil
}
var encodeTokenCfg []byte
var err error
var akInfo EncryptedCredentialInfo
if e.credentialPath != "" {
log.Infof("resolve encrypted credential %s", e.credentialPath)
if utils.IsWindowsOS() {
// NB(thxCode): since os.Stat has not worked as expected,
// we use os.Lstat instead of os.Stat here,
// ref to https://github.com/microsoft/Windows-Containers/issues/97#issuecomment-887713195.
_, err = os.Lstat(e.credentialPath)
} else {
_, err = os.Stat(e.credentialPath)
}
if err != nil {
return nil, fmt.Errorf("failed to read config %s, err: %w", e.credentialPath, err)
}
encodeTokenCfg, err = os.ReadFile(e.credentialPath)
if err != nil {
return nil, fmt.Errorf("failed to read token config, err: %w", err)
}
} else {
log.Infof("resolve secret %s/%s", e.secretNamespace, e.secretName)
var secret *corev1.Secret
err = retry.OnError(backoff.Backoff(backoff.WaitStsTokenReady), func(err error) bool {
if errors.IsNotFound(err) || errors.IsTooManyRequests(err) {
return true
}
return false
}, func() error {
secret, err = k8sclient.K8sClient.CoreV1().Secrets(e.secretNamespace).Get(context.Background(), e.secretName, metav1.GetOptions{})
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
var ok bool
encodeTokenCfg, ok = secret.Data["addon.token.config"]
if !ok {
return nil, fmt.Errorf("token is not found in addon.network.token")
}
}
err = json.Unmarshal(encodeTokenCfg, &akInfo)
if err != nil {
return nil, fmt.Errorf("error unmarshal token config: %w", err)
}
keyring := []byte(akInfo.Keyring)
ak, err := decrypt(akInfo.AccessKeyID, keyring)
if err != nil {
return nil, fmt.Errorf("failed to decode ak, err: %w", err)
}
sk, err := decrypt(akInfo.AccessKeySecret, keyring)
if err != nil {
return nil, fmt.Errorf("failed to decode sk, err: %w", err)
}
token, err := decrypt(akInfo.SecurityToken, keyring)
if err != nil {
return nil, fmt.Errorf("failed to decode token, err: %w", err)
}
layout := "2006-01-02T15:04:05Z"
t, err := time.Parse(layout, akInfo.Expiration)
if err != nil {
return nil, fmt.Errorf("failed to parse expiration time, err: %w", err)
}
return &Credential{
Credential: credentials.NewStsTokenCredential(string(ak), string(sk), string(token)),
Expiration: t,
}, nil
}
func (e *EncryptedCredentialProvider) Name() string {
return "EncryptedCredentialProvider"
}
func pks5UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}
func decrypt(s string, keyring []byte) ([]byte, error) {
cdata, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return nil, fmt.Errorf("failed to decode base64 string, err: %w", err)
}
block, err := aes.NewCipher(keyring)
if err != nil {
return nil, fmt.Errorf("failed to new cipher, err:%w", err)
}
blockSize := block.BlockSize()
iv := cdata[:blockSize]
blockMode := cipher.NewCBCDecrypter(block, iv)
origData := make([]byte, len(cdata)-blockSize)
blockMode.CryptBlocks(origData, cdata[blockSize:])
origData = pks5UnPadding(origData)
return origData, nil
}
type StsTokenCredentialProvider struct {
StsTokenCredential *credentials.StsTokenCredential
}
func NewStsTokenCredentialProvider(ak, sk, token string) *StsTokenCredentialProvider {
return &StsTokenCredentialProvider{
StsTokenCredential: credentials.NewStsTokenCredential(ak, sk, token),
}
}
func (e *StsTokenCredentialProvider) Resolve() (*Credential, error) {
return &Credential{
Credential: e.StsTokenCredential,
Expiration: time.Now().AddDate(100, 0, 0),
}, nil
}
func (e *StsTokenCredentialProvider) Name() string {
return "StsTokenCredentialProvider"
}