Programmatically accessing the EKS cluster API endpoint without a kubeconfig file

Jeremy Cowan
4 min readAug 18, 2023

There are times when you may want to programmatically call the Kubernetes API without loading the Kubernetes client SDK. For example, you may have a job that runs outside the cluster that needs to retrieve information about a Kubernetes object, like its status. For these types of lightweight operations, you can use a package from the aws-iam-authenticator Go module, for authentication. The token package creates a jwt token that you can include, along with the cluster certificate, in your HTTP requests to the Kubernetes API, as shown in the example below. The aws-iam-authenticator package accepts several parameters, including the region where the cluster is located, the cluster’s name, and the ARN of the role that you want to use for authentication.

Once you have a token, you load the cluster certificate into memory. You can fetch a cluster’s certificate by running the aws eks describe-cluster --name <cluster_name> --query 'cluster.certificateAuthority' --output text | base64 -d command. You could also get this information programmatically using the AWS SDK. See the DescribeCluster API for further information.

Next comes the assembly of the web request. If you’re not familiar with the Kubernetes API, you can get the HTTP request path for a kubectl command by appending -v6 flag to your request. For example, the command kubectl get pods <pod_name> -v6 produces the following output:

GET https://<cluster_id>.sk1.us-west-2.eks.amazonaws.com/api/v1/namespaces/default/pods/<pod_name> 200 OK in 190 milliseconds

The method type for this request is GET and the path is api/v1/namespaces/default/pods/<pod_name> . When you’re done putting this all together you get program that looks like the example below:

package main

import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"sigs.k8s.io/aws-iam-authenticator/pkg/token"
)

func main() {
g, _ := token.NewGenerator(false, false)
tk, err := g.GetWithOptions(&token.GetTokenOptions{
Region: "<region>",
ClusterID: "<cluster_name>",
AssumeRoleARN: "arn:aws:iam::<account_id>:role/<role_name>",
Session: nil,
})

if err != nil {
log.Fatal(err)
}
fmt.Println(tk.Token)

caCert, err := ioutil.ReadFile("ca.crt") // ca.crt is certificate authority for the cluster
if err != nil {
log.Fatal(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)

client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
},
},
}

req, err := http.NewRequest("<http_method>", "<cluster_api_server_endpoint>/<api_path>", nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+tk.Token)
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
bodyText, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", bodyText)
}

The output from this program, when getting information about a pod, is a json blob that looks like this:

{
"kind": "Pod",
"apiVersion": "v1",
"metadata": {
"name": "nginx-6f98b676bf-h5r4q",
"generateName": "nginx-6f98b676bf-",
"namespace": "default",
"uid": "0904491e-afe4-499d-a298-9ff7867430b3",
"resourceVersion": "261888030",
"creationTimestamp": "2023-08-08T00:02:49Z",
"labels": {
"app": "nginx",
"pod-template-hash": "6f98b676bf"
},
"ownerReferences": [{
"apiVersion": "apps/v1",
"kind": "ReplicaSet",
"name": "nginx-6f98b676bf",
"uid": "aed87c51-af65-4549-86fe-1bccd9573830",
"controller": true,
"blockOwnerDeletion": true
}],
"managedFields": [{
"manager": "kube-controller-manager",
"operation": "Update",
"apiVersion": "v1",
"time": "2023-08-08T00:02:49Z",
"fieldsType": "FieldsV1",
"fieldsV1": {
"f:metadata": {
"f:generateName": {},
"f:labels": {
".": {},
"f:app": {},
"f:pod-template-hash": {}
},
"f:ownerReferences": {
".": {},
"k:{\"uid\":\"aed87c51-af65-4549-86fe-1bccd9573830\"}": {}
}
},
"f:spec": {
"f:containers": {
"k:{\"name\":\"ratifydemo\"}": {
".": {},
"f:image": {},
"f:imagePullPolicy": {},
"f:name": {},
"f:resources": {},
"f:terminationMessagePath": {},
"f:terminationMessagePolicy": {}
}
},
"f:dnsPolicy": {},
"f:enableServiceLinks": {},
"f:restartPolicy": {},
"f:schedulerName": {},
"f:securityContext": {},
"f:terminationGracePeriodSeconds": {}
}
}
}, {
"manager": "kube-scheduler",
"operation": "Update",
"apiVersion": "v1",
"time": "2023-08-08T00:02:49Z",
"fieldsType": "FieldsV1",
"fieldsV1": {
"f:status": {
"f:conditions": {
".": {},
"k:{\"type\":\"PodScheduled\"}": {
".": {},
"f:lastProbeTime": {},
"f:lastTransitionTime": {},
"f:message": {},
"f:reason": {},
"f:status": {},
"f:type": {}
}
}
}
},
"subresource": "status"
}, {
"manager": "kubelet",
"operation": "Update",
"apiVersion": "v1",
"time": "2023-08-18T15:09:03Z",
"fieldsType": "FieldsV1",
"fieldsV1": {
"f:status": {
"f:conditions": {
"k:{\"type\":\"ContainersReady\"}": {
".": {},
"f:lastProbeTime": {},
"f:lastTransitionTime": {},
"f:status": {},
"f:type": {}
},
"k:{\"type\":\"Initialized\"}": {
".": {},
"f:lastProbeTime": {},
"f:lastTransitionTime": {},
"f:status": {},
"f:type": {}
},
"k:{\"type\":\"Ready\"}": {
".": {},
"f:lastProbeTime": {},
"f:lastTransitionTime": {},
"f:status": {},
"f:type": {}
}
},
"f:containerStatuses": {},
"f:hostIP": {},
"f:phase": {},
"f:podIP": {},
"f:podIPs": {
".": {},
"k:{\"ip\":\"192.168.107.222\"}": {
".": {},
"f:ip": {}
}
},
"f:startTime": {}
}
},
"subresource": "status"
}]
},
"spec": {
"volumes": [{
"name": "kube-api-access-9mq7f",
"projected": {
"sources": [{
"serviceAccountToken": {
"expirationSeconds": 3607,
"path": "token"
}
}, {
"configMap": {
"name": "kube-root-ca.crt",
"items": [{
"key": "ca.crt",
"path": "ca.crt"
}]
}
}, {
"downwardAPI": {
"items": [{
"path": "namespace",
"fieldRef": {
"apiVersion": "v1",
"fieldPath": "metadata.namespace"
}
}]
}
}],
"defaultMode": 420
}
}],
"containers": [{
"name": "ratifydemo",
"image": "<account_id>.dkr.ecr.us-west-2.amazonaws.com/ratifydemo@sha256:ba2a088ba1f8861143dfdab8e8e84a65dd8c7cc83c2cec690168741576715690",
"resources": {},
"volumeMounts": [{
"name": "kube-api-access-9mq7f",
"readOnly": true,
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
}],
"terminationMessagePath": "/dev/termination-log",
"terminationMessagePolicy": "File",
"imagePullPolicy": "IfNotPresent"
}],
"restartPolicy": "Always",
"terminationGracePeriodSeconds": 30,
"dnsPolicy": "ClusterFirst",
"serviceAccountName": "default",
"serviceAccount": "default",
"nodeName": "ip-192-168-102-46.us-west-2.compute.internal",
"securityContext": {},
"schedulerName": "default-scheduler",
"tolerations": [{
"key": "node.kubernetes.io/not-ready",
"operator": "Exists",
"effect": "NoExecute",
"tolerationSeconds": 300
}, {
"key": "node.kubernetes.io/unreachable",
"operator": "Exists",
"effect": "NoExecute",
"tolerationSeconds": 300
}],
"priority": 0,
"enableServiceLinks": true,
"preemptionPolicy": "PreemptLowerPriority"
},
"status": {
"phase": "Running",
"conditions": [{
"type": "Initialized",
"status": "True",
"lastProbeTime": null,
"lastTransitionTime": "2023-08-08T00:02:51Z"
}, {
"type": "Ready",
"status": "True",
"lastProbeTime": null,
"lastTransitionTime": "2023-08-18T15:09:03Z"
}, {
"type": "ContainersReady",
"status": "True",
"lastProbeTime": null,
"lastTransitionTime": "2023-08-18T15:09:03Z"
}, {
"type": "PodScheduled",
"status": "True",
"lastProbeTime": null,
"lastTransitionTime": "2023-08-08T00:02:51Z"
}],
"hostIP": "192.168.102.46",
"podIP": "192.168.107.222",
"podIPs": [{
"ip": "192.168.107.222"
}],
"startTime": "2023-08-08T00:02:51Z",
"containerStatuses": [{
"name": "ratifydemo",
"state": {
"running": {
"startedAt": "2023-08-18T15:09:02Z"
}
},
"lastState": {
"terminated": {
"exitCode": 0,
"reason": "Completed",
"startedAt": "2023-08-18T14:39:02Z",
"finishedAt": "2023-08-18T15:09:02Z",
"containerID": "containerd://57ffc5117bd298fc0a741bac647439d46bfd5838bfde04e49bdec657448a62a8"
}
},
"ready": true,
"restartCount": 510,
"image": "sha256:1ce36985e63c17a55524a01a4fb92c837c62251260ed6b1216f053bff7d1d2aa",
"imageID": "<account_id>.dkr.ecr.us-west-2.amazonaws.com/ratifydemo@sha256:ba2a088ba1f8861143dfdab8e8e84a65dd8c7cc83c2cec690168741576715690",
"containerID": "containerd://2065987bcde2398026e6f78a8f5655ac823368a8c087eac51b499d26b4c01158",
"started": true
}],
"qosClass": "BestEffort"
}
}

Happy programming!

--

--

Jeremy Cowan
Jeremy Cowan

Written by Jeremy Cowan

Jeremy Cowan is a Principal Container Specialist at AWS

No responses yet