github.com/khulnasoft-lab/kube-bench@v0.2.1-0.20240330183753-9df52345ae58/cmd/kubernetes_version.go (about) 1 package cmd 2 3 import ( 4 "crypto/tls" 5 "encoding/json" 6 "encoding/pem" 7 "fmt" 8 "io" 9 "net/http" 10 "os" 11 "strings" 12 "time" 13 14 "github.com/golang/glog" 15 ) 16 17 type KubeVersion struct { 18 Major string 19 Minor string 20 baseVersion string 21 GitVersion string 22 } 23 24 func (k *KubeVersion) BaseVersion() string { 25 if k.baseVersion != "" { 26 return k.baseVersion 27 } 28 // Some provides return the minor version like "15+" 29 minor := strings.Replace(k.Minor, "+", "", -1) 30 ver := fmt.Sprintf("%s.%s", k.Major, minor) 31 k.baseVersion = ver 32 return ver 33 } 34 35 func getKubeVersionFromRESTAPI() (*KubeVersion, error) { 36 glog.V(2).Info("Try to get version from Rest API") 37 k8sVersionURL := getKubernetesURL() 38 serviceaccount := "/var/run/secrets/kubernetes.io/serviceaccount" 39 cacertfile := fmt.Sprintf("%s/ca.crt", serviceaccount) 40 tokenfile := fmt.Sprintf("%s/token", serviceaccount) 41 42 tlsCert, err := loadCertificate(cacertfile) 43 if err != nil { 44 glog.V(2).Infof("Failed loading certificate Error: %s", err) 45 return nil, err 46 } 47 48 tb, err := os.ReadFile(tokenfile) 49 if err != nil { 50 glog.V(2).Infof("Failed reading token file Error: %s", err) 51 return nil, err 52 } 53 token := strings.TrimSpace(string(tb)) 54 55 data, err := getWebDataWithRetry(k8sVersionURL, token, tlsCert) 56 if err != nil { 57 glog.V(2).Infof("Failed to get data Error: %s", err) 58 return nil, err 59 } 60 61 k8sVersion, err := extractVersion(data) 62 if err != nil { 63 return nil, err 64 } 65 return k8sVersion, nil 66 } 67 68 // The idea of this function is so if Kubernetes DNS is not completely seetup and the 69 // Container where kube-bench is running needs time for DNS configure. 70 // Basically try 10 times, waiting 1 second until either it is successful or it fails. 71 func getWebDataWithRetry(k8sVersionURL, token string, cacert *tls.Certificate) (data []byte, err error) { 72 tries := 0 73 // We retry a few times in case the DNS service has not had time to come up 74 for tries < 10 { 75 data, err = getWebData(k8sVersionURL, token, cacert) 76 if err == nil { 77 return 78 } 79 tries++ 80 time.Sleep(1 * time.Second) 81 } 82 83 return 84 } 85 86 type VersionResponse struct { 87 Major string 88 Minor string 89 GitVersion string 90 GitCommit string 91 GitTreeState string 92 BuildDate string 93 GoVersion string 94 Compiler string 95 Platform string 96 } 97 98 func extractVersion(data []byte) (*KubeVersion, error) { 99 vrObj := &VersionResponse{} 100 glog.V(2).Info(fmt.Sprintf("vd: %s\n", string(data))) 101 err := json.Unmarshal(data, vrObj) 102 if err != nil { 103 return nil, err 104 } 105 glog.V(2).Info(fmt.Sprintf("vrObj: %#v\n", vrObj)) 106 107 return &KubeVersion{ 108 Major: vrObj.Major, 109 Minor: vrObj.Minor, 110 GitVersion: vrObj.GitVersion, 111 }, nil 112 } 113 114 func getWebData(srvURL, token string, cacert *tls.Certificate) ([]byte, error) { 115 glog.V(2).Info(fmt.Sprintf("getWebData srvURL: %s\n", srvURL)) 116 117 tlsConf := &tls.Config{ 118 Certificates: []tls.Certificate{*cacert}, 119 InsecureSkipVerify: true, 120 } 121 tr := &http.Transport{ 122 TLSClientConfig: tlsConf, 123 } 124 client := &http.Client{Transport: tr} 125 req, err := http.NewRequest(http.MethodGet, srvURL, nil) 126 if err != nil { 127 return nil, err 128 } 129 130 authToken := fmt.Sprintf("Bearer %s", token) 131 req.Header.Set("Authorization", authToken) 132 133 resp, err := client.Do(req) 134 if err != nil { 135 glog.V(2).Info(fmt.Sprintf("HTTP ERROR: %v\n", err)) 136 return nil, err 137 } 138 defer resp.Body.Close() 139 140 if resp.StatusCode != http.StatusOK { 141 glog.V(2).Info(fmt.Sprintf("URL:[%s], StatusCode:[%d] \n Headers: %#v\n", srvURL, resp.StatusCode, resp.Header)) 142 err = fmt.Errorf("URL:[%s], StatusCode:[%d]", srvURL, resp.StatusCode) 143 return nil, err 144 } 145 146 return io.ReadAll(resp.Body) 147 } 148 149 func loadCertificate(certFile string) (*tls.Certificate, error) { 150 cacert, err := os.ReadFile(certFile) 151 if err != nil { 152 return nil, err 153 } 154 155 var tlsCert tls.Certificate 156 block, _ := pem.Decode(cacert) 157 if block == nil { 158 return nil, fmt.Errorf("unable to Decode certificate") 159 } 160 161 glog.V(2).Info("Loading CA certificate") 162 tlsCert.Certificate = append(tlsCert.Certificate, block.Bytes) 163 return &tlsCert, nil 164 } 165 166 func getKubernetesURL() string { 167 k8sVersionURL := "https://kubernetes.default.svc/version" 168 169 // The following provides flexibility to use 170 // K8S provided variables is situations where 171 // hostNetwork: true 172 if !isEmpty(os.Getenv("KUBE_BENCH_K8S_ENV")) { 173 k8sHost := os.Getenv("KUBERNETES_SERVICE_HOST") 174 k8sPort := os.Getenv("KUBERNETES_SERVICE_PORT_HTTPS") 175 if !isEmpty(k8sHost) && !isEmpty(k8sPort) { 176 return fmt.Sprintf("https://%s:%s/version", k8sHost, k8sPort) 177 } 178 179 glog.V(2).Info("KUBE_BENCH_K8S_ENV is set, but environment variables KUBERNETES_SERVICE_HOST or KUBERNETES_SERVICE_PORT_HTTPS are not set") 180 } 181 182 return k8sVersionURL 183 }