如何在Pod容器内访问Kubernetes API?

140

我曾经能够卷曲

https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/api/v1beta3/namespaces/default/

我使用了我的基本URL,但在kubernetes 0.18.0中它给了我“未经授权”的错误。奇怪的是,如果我使用API机器的外部IP地址 (http://172.17.8.101:8080/api/v1beta3/namespaces/default/),它就可以正常工作。


你在哪里运行你的集群(GCE,AWS等),使用什么基本操作系统(debian,CoreOS等)? - Robert Bailey
Vagrant/CoreOS...我最终会将它迁移到AWS/CoreOS。 - tslater
1
$KUBERNETES_SERVICE_HOST$KUBERNETES_PORT_443_TCP_PORT变量从哪里来? - ruediste
12个回答

168

在官方文档中我找到了这个链接:

https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod

显然我缺少了一个以前版本的Kubernetes不需要的安全令牌。基于此,我想出了一个比运行代理或在我的容器上安装golang更简单的解决方案。查看此示例,可以从api获取当前容器的信息:

KUBE_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -sSk -H "Authorization: Bearer $KUBE_TOKEN" \
      https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/api/v1/namespaces/default/pods/$HOSTNAME

我还使用一个简单的二进制文件jq (http://stedolan.github.io/jq/download/),用于解析JSON以在bash脚本中使用。


5
针对最近部署的集群,您可能需要将 v1beta3 更改为 v1 - Eyal Levin
7
请注意,这个curl命令将不安全地连接到apiserver(这使得中间人可以拦截承载令牌),因此您只应在pod和apiserver之间的网络是完全可信的情况下使用它。否则,您应该向curl传递--cacert标志,以便curl验证apiserver提供的证书。 - Robert Bailey
1
我必须使用KUBERNETES_SERVICE_HOST=kubernetes.default$KUBERNETES_443_TCP_PORT=443NAMESPACE==$(</var/run/secrets/kubernetes.io/serviceaccount/namespace)。URL是https://kubernetes.default:443/api/v1/namespaces/$NAMESPACE/pods/$HOSTNAME。请注意,API版本设置为v1而不是v1beta3,并且默认命名空间被替换为$NAMESPACE。 - ruediste

92

每个Pod自动应用了一个服务账户,允许其访问api服务器。该服务账户提供了客户端凭据(以Bearer Token形式)和证书授权机构证书(用于签署apiserver提供的证书)。有了这两个信息,您可以创建一个安全的、经过身份验证的连接到apiserver,而不需要使用curl -k(也称为curl --insecure):

curl -v --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" https://kubernetes.default.svc/

3
需要注意的是,在服务账户中同时存在 cacert 和 token,需要在启动时向复制控制器提供“--root-ca-file=”参数。(大多数 Kubernetes 安装程序都会自动处理此问题)。有关更多详细信息,请参见此处的讨论:https://github.com/kubernetes/kubernetes/issues/10265 - JKnight
9
我正在从一个不同命名空间的 Pod 访问 API 服务器。因此,我必须使用 https://kubernetes.default/ 作为主机。 - ruediste
官方主机是 kubernetes.default.svc,如 https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod 中所述。 - Martin Tapp

20

使用 Python Kubernetes 客户端..

    from kubernetes import client, config

    config.load_incluster_config()
    v1_core = client.CoreV1Api()

1
谢谢!这里有一个小的代码库,基于您的回答,提供了一个示例,使得使用这段代码更加简单。 - Omer Levi Hevroni

13

wget 版本:

KUBE_TOKEN=$(</var/run/secrets/kubernetes.io/serviceaccount/token)    
wget -vO- --ca-certificate /var/run/secrets/kubernetes.io/serviceaccount/ca.crt  --header "Authorization: Bearer $KUBE_TOKEN" https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/api/v1/namespaces/default/pods/$HOSTNAME

7

从容器内部,可以直接访问 Kubernetes API 服务器,网址为 "https://kubernetes.default"。默认情况下,它使用 "default service account" 来访问 API 服务器。

因此,我们还需要传递 "ca cert" 和 "default service account token" 来验证访问 API 服务器的身份。

证书文件存储在容器内的以下位置: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt

以及默认服务帐户令牌位置为: /var/run/secrets/kubernetes.io/serviceaccount/token

您可以使用 nodejs kubbernetes godaddy client

let getRequestInfo = () => {
    return {
        url: "https://kubernetes.default",
        ca:   fs.readFileSync('/var/run/secrets/kubernetes.io/serviceaccount/ca.crt').toString(),
        auth: {
            bearer: fs.readFileSync('/var/run/secrets/kubernetes.io/serviceaccount/token').toString(),
        },
        timeout: 1500
    };
}

let initK8objs = () =>{
    k8obj = getRequestInfo();
    k8score = new Api.Core(k8obj),
    k8s = new Api.Api(k8obj);
}


6
上述提到的细节中最重要的补充是,您尝试访问API服务器的Pod应具备RBAC功能。
k8s系统中的每个实体都由服务帐户(类似于用户帐户用于用户)标识。根据RBAC功能,服务帐户令牌(/var/run/secrets/kubernetes.io/serviceaccount/token)被填充。kube-api绑定(例如pykube)可以在连接到kube-api服务器时将此令牌作为输入。如果Pod具有正确的RBAC功能,则该Pod将能够与kube-api服务器建立连接。

5

当我尝试使用Go代码从Pod内部访问API时,遇到了这个问题。下面是我实现的解决方法,如果有人想要在Go中使用它,可以参考一下。

示例使用一个Pod资源,如果您正在使用原生Kubernetes对象,则应使用client-go库。这段代码对于那些使用CustomResourceDefintions的人更有用。

serviceHost := os.GetEnv("KUBERNETES_SERVICE_HOST")
servicePort := os.GetEnv("KUBERNETES_SERVICE_PORT")
apiVersion := "v1" // For example
namespace := default // For example
resource := "pod" // For example
httpMethod := http.MethodGet // For Example

url := fmt.Sprintf("https://%s:%s/apis/%s/namespaces/%s/%s", serviceHost, servicePort, apiVersion, namespace, resource)

u, err := url.Parse(url)
if err != nil {
  panic(err)
}
req, err := http.NewRequest(httpMethod, u.String(), bytes.NewBuffer(payload))
if err != nil {
    return err
}

caToken, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
if err != nil {
    panic(err) // cannot find token file
}

req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", string(caToken)))

caCertPool := x509.NewCertPool()
caCert, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt")
if err != nil {
    return panic(err) // Can't find cert file
}
caCertPool.AppendCertsFromPEM(caCert)

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

resp, err := client.Do(req)
if err != nil {
    log.Printf("sending helm deploy payload failed: %s", err.Error())
    return err
}
defer resp.Body.Close()

// Check resp.StatusCode
// Check resp.Status

5

我在GKE上遇到了类似的认证问题,Python脚本突然抛出异常。对我有效的解决方案是通过角色授予权限给Pods。

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: fabric8-rbac
subjects:
  - kind: ServiceAccount
  # Reference to upper's `metadata.name`
  name: default
  # Reference to upper's `metadata.namespace`
  namespace: default
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io

更多信息请点击此处


5

这是来自Kubernetes In Action书籍中的内容。

您需要注意身份验证。API服务器本身会告诉您未经授权无法访问它,因为它不知道您是谁

要进行身份验证,您需要一个身份验证令牌。幸运的是,该令牌通过先前提到的默认令牌密钥提供,并存储在密钥卷中的令牌文件中。

您将使用令牌来访问API服务器。首先,将令牌加载到环境变量中:

root@myhome:/# TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

令牌现在存储在TOKEN环境变量中。在向API服务器发送请求时,您可以使用它:
root@curl:/# curl -H "Authorization: Bearer $TOKEN"  https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/api/v1/namespaces/default/pods/$HOSTNAME
   {  "paths": 
      [    
        "/api",    
        "/api/v1",   
        "/apis",    
        "/apis/apps",    
        "/apis/apps/v1beta1",    
        "/apis/authorization.k8s.io",        
         ...    
        "/ui/",    
        "/version"  
      ]
  }

3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接