wd and cc

-- Good good study, day day up!

Use Golang to Generate Eks Token

#Golang #Eks #Kubernetes

在研究 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
comments powered by Disqus