diff --git a/cmd/root.go b/cmd/root.go index 387b936..75d9428 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -3,8 +3,10 @@ package cmd import ( "fmt" "github.com/spf13/cobra" + "io/ioutil" "k8s.io/client-go/tools/clientcmd" "kubeletctl/pkg/api" + "log" "net/url" restclient "k8s.io/client-go/rest" "os" @@ -23,6 +25,8 @@ var ( caFlag string certFlag string keyFlag string + tokenFlag string + tokenFileFlag string HttpFlag bool IgnoreDefaultKubeConfigFlag bool //BodyContentFlag string @@ -44,6 +48,12 @@ var RootCmd = &cobra.Command{ // List all pods from kubelet kubeletctl pods --server 123.123.123.123 + // List all pods from kubelet with token + kubeletctl pods --token --server 123.123.123.123 + + // List all pods from kubelet with token file + kubeletctl pods --token-file /var/run/secrets/kubernetes.io/serviceaccount/token --server 123.123.123.123 + // Searching for service account token in each accessible container kubeletctl scan token --server 123.123.123.123 @@ -91,6 +101,8 @@ func init() { RootCmd.PersistentFlags().BoolVarP(&IgnoreDefaultKubeConfigFlag, "ignoreconfig", "i", false, "Ignore the default KUBECONFIG environment variable or location ~/.kube") //RootCmd.PersistentFlags().StringVarP(&BodyContentFlag, "body", "b", "", "This is the body message. Should be used in POST or PUT requests.") + RootCmd.PersistentFlags().StringVarP(&tokenFileFlag, "token-file", "f", "", "Service account Token (JWT) file path") + RootCmd.PersistentFlags().StringVarP(&tokenFlag, "token", "t", "", "Service account Token (JWT) to insert") RootCmd.PersistentFlags().StringVarP(&caFlag, "cacert", "", "", "CA certificate (example: /etc/kubernetes/pki/ca.crt )") RootCmd.PersistentFlags().StringVarP(&certFlag, "cert", "", "", "Private key (example: /var/lib/kubelet/pki/kubelet-client-current.pem)") RootCmd.PersistentFlags().StringVarP(&keyFlag, "key", "", "", "Digital certificate (example: /var/lib/kubelet/pki/kubelet-client-current.pem)") @@ -100,6 +112,16 @@ func init() { //cobra.MarkFlagRequired(pf, "server") } +func readTokenFromFile(filePath string) string { + data, err := ioutil.ReadFile(filePath) + if err != nil { + fmt.Print("[*] Failed to read file") + log.Fatal(err) + } + + return string(data) +} + const KUBELET_DEFAULT_PORT = "10250" func initConfig() { @@ -127,6 +149,14 @@ func initConfig() { CAFile: caFlag, }, } + } else if tokenFlag != "" { + config = &restclient.Config{ + BearerToken: tokenFlag, + } + } else if tokenFileFlag != "" { + config = &restclient.Config{ + BearerToken: readTokenFromFile(tokenFileFlag), + } } else { if !IgnoreDefaultKubeConfigFlag { kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( diff --git a/pkg/api/requests.go b/pkg/api/requests.go index bf93bf0..4d007c8 100644 --- a/pkg/api/requests.go +++ b/pkg/api/requests.go @@ -4,17 +4,17 @@ import ( "bytes" "crypto/tls" "crypto/x509" + "fmt" "io/ioutil" restclient "k8s.io/client-go/rest" "log" "net/http" "os" "time" - "fmt" ) var GlobalClient *http.Client - +var GlobalBearerToken string // a struct to hold the result from each request including an index // which will be used for sorting the results after they come in type Result struct { @@ -30,6 +30,7 @@ const ( GET HTTPVerb = "GET" POST HTTPVerb = "POST" DELETE HTTPVerb = "DELETE" + PUT HTTPVerb = "PUT" ) func InitHttpClient(config *restclient.Config) { @@ -37,7 +38,12 @@ func InitHttpClient(config *restclient.Config) { insecure := true var tr *http.Transport - if config != nil { + if config != nil && config.BearerToken != "" { + GlobalBearerToken = config.BearerToken + } + + // No need to check config.BearerTokenFile because it already being checked in root.go + if config != nil && config.BearerToken == "" { fmt.Fprintln(os.Stderr, "[*] Using KUBECONFIG environment variable\n[*] You can ignore it by modifying the KUBECONFIG environment variable, file \"~/.kube/config\" or use the \"-i\" switch") tr = getHttpTransportWithCertificates(config, insecure) } else { @@ -55,6 +61,7 @@ func InitHttpClient(config *restclient.Config) { } } + func getHttpTransportWithCertificates(config *restclient.Config, insecure bool) *http.Transport { var cert tls.Certificate var err error @@ -97,33 +104,37 @@ func getHttpTransportWithCertificates(config *restclient.Config, insecure bool) return tr } -func GetRequest(client *http.Client, url string) (*http.Response, error) { - req, _ := http.NewRequest("GET", url, nil) +func DoGenericRequest(req *http.Request, client *http.Client) (*http.Response, error){ + if GlobalBearerToken != "" { + req.Header.Set("Authorization", "Bearer " + GlobalBearerToken) + } - //req.Header.Set("Authorization", "Bearer " + BEARER_TOKEN) resp, err := (*client).Do(req) return resp, err } +func GetRequest(client *http.Client, url string) (*http.Response, error) { + req, _ := http.NewRequest("GET", url, nil) + + return DoGenericRequest(req, client) +} + func PutRequest(client *http.Client, url string, bodyData []byte) (*http.Response, error) { req, _ := http.NewRequest("PUT", url, bytes.NewBuffer(bodyData)) req.Header.Set("Content-Type", "text/plain") - resp, err := (*client).Do(req) - - return resp, err + return DoGenericRequest(req, client) } func PostRequest(client *http.Client, url string, bodyData []byte) (*http.Response, error) { req, _ := http.NewRequest("POST", url, bytes.NewBuffer(bodyData)) //req.Header.Set("Authorization", "Bearer " + BEARER_TOKEN) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - resp, err := (*client).Do(req) - - return resp, err + return DoGenericRequest(req, client) } + /* func PostRequest2(client *http.Client, url string, bodyData []byte) (*http.Response, error){ req, _ := http.NewRequest("POST", url, bytes.NewBuffer(bodyData))