Use Golang to Generate Eks Token
在研究 kubeconfig 配置文件的时候,看到如果使用 aws 命令来生成 token 的话,就需要传递 cluster name 和 region 给到 aws 命令。这样就相等于每个集群都配置一个相应的命令,感觉有点傻好像。
研究的过程中发现 kubeconfig 的配置里面有一个 KUBERNETES_EXEC_INFO 这样的变量,可以携带 cluster 中的配置给到执行的命令。这样好像就有可能使用这个特性来简化命令的配置了。
但是 aws 命令是不支持那个环境变量的,简单的做法就是写一个 shell 脚本做 wrapper 来读取环境变量然后传递给 aws 命令。但是这样好像有点蛋疼,我就在想,是不是可以自己写一个命令来做这个事情呢,如果可以的话,还省去了对 aws 命令的依赖。
下面是主要的代码。eks 比较搞笑的是,他们生成的并不是真正的 token,而是一个签名之后用来获取 sts credential 的 url。我中间遇到过生成的 token 有时候会很快过期的问题,不过后来也不知道怎么没有问题了。
1func (tsdb *Tsdb) generateClusterToken(cluster string, region string, profile string) (string, error) {
2 if len(cluster) == 0 {
3 kubeenv := os.Getenv("KUBERNETES_EXEC_INFO")
4 if len(kubeenv) == 0 {
5 log.Fatal().Msg("No kubeenv found")
6 }
7 var kubeenvJson authenticationv1beta1.ExecCredential
8 json.Unmarshal([]byte(kubeenv), &kubeenvJson)
9
10 var myconfig TsdbClusterExtension
11 json.Unmarshal(kubeenvJson.Spec.Cluster.Config.Raw, &myconfig)
12
13 cluster = myconfig.ClusterName
14 region = myconfig.Region
15 profile = myconfig.Profile
16 }
17
18 url, _ := getPresignUrl(profile, cluster, region)
19
20 token := "k8s-aws-v1." + base64.RawURLEncoding.EncodeToString([]byte(url))
21 expirationTime := metav1.NewTime(time.Now().Add(time.Hour))
22
23 execCredential := authenticationv1beta1.ExecCredential{
24 TypeMeta: metav1.TypeMeta{
25 APIVersion: ExecPluginAPIVersion,
26 Kind: "ExecCredential",
27 },
28 Status: &authenticationv1beta1.ExecCredentialStatus{
29 Token: token,
30 ExpirationTimestamp: &expirationTime,
31 },
32 }
33 tokenCR, err := json.MarshalIndent(execCredential, "", " ")
34 return string(tokenCR), err
35}
36
37func getPresignUrl(profile string, clusterName string, region string) (string, error) {
38 if len(profile) == 0 {
39 profile = "default"
40 }
41 if len(region) == 0 {
42 region = "us-east-2"
43 }
44 cfg, _ := config.LoadDefaultConfig(context.TODO(),
45 config.WithRegion(region),
46 config.WithSharedConfigProfile(profile))
47
48 u := url.URL{
49 Scheme: "https",
50 Host: "sts." + cfg.Region + ".amazonaws.com",
51 Path: "/",
52 }
53
54 q := url.Values{}
55 q.Set("Action", "GetCallerIdentity")
56 q.Set("Version", "2011-06-15")
57 q.Set("X-Amz-Expires", "60")
58 u.RawQuery = q.Encode()
59
60 // set header
61 req, _ := http.NewRequest(http.MethodGet, u.String(), nil)
62 req.Header.Set("x-k8s-aws-id", clusterName)
63
64 h := sha256.New()
65 payloadHash := hex.EncodeToString(h.Sum(nil)) // e3b0c4...b855
66
67 //v4 Presign
68 creds, err := cfg.Credentials.Retrieve(context.TODO())
69 if err != nil {
70 return "", err
71 }
72
73 signer := v4.NewSigner()
74 signedURL, _, err := signer.PresignHTTP(context.TODO(), creds, req,
75 payloadHash, "sts", cfg.Region, time.Now().UTC())
76 if err != nil {
77 return "", err
78 }
79 return signedURL, nil
80}Kubeconfig 的配置文件中需要类似这样的配置
1clusters:
2- cluster:
3 extensions:
4 - extension:
5 clusterName: xx
6 profile: yy
7 region: us-east-2
8 name: client.authentication.k8s.io/exec