¿Cómo accedo a la API de Kubernetes desde un contenedor de pod?


118

Solía ​​poder rizarme

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

como mi URL base, pero en kubernetes 0.18.0 me da "no autorizado". Lo extraño es que si usé la dirección IP externa de la máquina API ( http://172.17.8.101:8080/api/v1beta3/namespaces/default/), funciona bien.


¿Dónde está ejecutando su clúster (GCE, AWS, etc.) y qué sistema operativo base (debian, CoreOS, etc.)?
Robert Bailey

Vagrant / CoreOS ... eventualmente lo moveré a AWS / CoreOS
tslater

¿De dónde proceden las variables $KUBERNETES_SERVICE_HOSTy $KUBERNETES_PORT_443_TCP_PORT?
ruediste

Encontré esta guía increíble para 101 sobre cuentas de servicio, roles y enlaces de roles developer.ibm.com/recipes/tutorials/… . La última sección detalla cómo podemos acceder al formulario de la API k8 dentro de los pods.
viv

Respuestas:


132

En la documentación oficial encontré esto:

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

Aparentemente, me faltaba un token de seguridad que no necesitaba en una versión anterior de Kubernetes. A partir de eso, ideé lo que creo que es una solución más simple que ejecutar un proxy o instalar golang en mi contenedor. Vea este ejemplo que obtiene la información, de la api, para el contenedor actual:

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

También utilizo incluir un binario simple, jq ( http://stedolan.github.io/jq/download/ ), para analizar el json para usarlo en scripts bash.


5
Para los clústeres desplegadas recientemente es posible que desee cambiar v1beta3av1
Eyal Levin

6
Tenga en cuenta que este comando curl se conectará de forma insegura al apiserver (haciendo posible que un intermediario intercepte el token de portador), por lo que solo debe usarlo si la red entre el pod y el apiserver es de plena confianza. De lo contrario, debe pasar la --cacertbandera a curl para que curl valide el certificado presentado por el apiserver.
Robert Bailey

1
Tuve que usar KUBERNETES_SERVICE_HOST=kubernetes.default, $KUBERNETES_443_TCP_PORT=443, NAMESPACE == $ (</ var / run / secretos / kubernetes.io / ServiceAccount / espacio de nombres) . The URL was kubernetes.default: 443 / API / v1 / espacios de nombres / $ NAMESPACE / vainas / ... `. Tenga en cuenta que la versión de API se establece en v1 en lugar de v1beta3 y el espacio de nombres predeterminado se reemplazó con $ NAMESPACE.
ruediste

74

Cada pod tiene una cuenta de servicio aplicada automáticamente que le permite acceder al apiserver. La cuenta de servicio proporciona tanto las credenciales del cliente, en forma de token de portador, como el certificado de la autoridad de certificación que se utilizó para firmar el certificado presentado por el apiserver. Con estos dos datos, puede crear una conexión segura y autenticada con el apisever sin usar curl -k(también conocido como 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/

2
Cabe señalar que para que el cacert y el token existan en la cuenta de servicio, se debe proporcionar un --root-ca-file=argumento al controlador de replicación cuando se inicie. (esto se maneja automáticamente en la mayoría de los instaladores de kubernetes). Consulte la discusión aquí para obtener más detalles: github.com/kubernetes/kubernetes/issues/10265
JKnight

7
Accedía al servidor API desde un pod con un espacio de nombres diferente. Por lo tanto, tuve que usar https://kubernetes.default/como anfitrión
ruediste

El anfitrión oficial está kubernetes.default.svcdocumentado en kubernetes.io/docs/tasks/access-application-cluster/…
Martin Tapp

17

Usando el cliente Python kubernetes ...

from kubernetes import client, config

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

1
¡Gracias! Aquí hay un pequeño repositorio con un ejemplo, basado en su respuesta, para que sea más sencillo jugar con este código.
Omer Levi Hevroni

10

versión 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

6

El apéndice más importante a los detalles ya mencionados anteriormente es que el pod desde el que está intentando acceder al servidor API debe tener las capacidades RBAC para hacerlo.

Cada entidad en el sistema k8s se identifica mediante una cuenta de servicio (como la cuenta de usuario que se utiliza para los usuarios). Según las capacidades de RBAC, se completa el token de la cuenta de servicio (/var/run/secrets/kubernetes.io/serviceaccount/token). Los enlaces kube-api (por ejemplo, pykube) pueden tomar este token como entrada cuando se crea una conexión con los servidores kube-api. Si el pod tiene las capacidades de RBAC correctas, el pod podría establecer la conexión con el servidor kube-api.


5

Me encontré con este problema al intentar acceder a la API desde dentro de un pod usando Go Code. A continuación se muestra lo que implementé para que eso funcione, en caso de que alguien se encuentre con esta pregunta y quiera usar Go también.

El ejemplo usa un recurso de pod, para el cual debe usar la client-gobiblioteca si está trabajando con objetos nativos de Kubernetes. El código es más útil para quienes trabajan con 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

4

Desde el interior del pod, se puede acceder al servidor de la API de kubernetes directamente en " https: //kubernetes.default ". De forma predeterminada, utiliza la "cuenta de servicio predeterminada" para acceder al servidor api.

Por lo tanto, también necesitamos pasar un "certificado de ca" y un "token de cuenta de servicio predeterminado" para autenticarnos con el servidor api.

El archivo de certificado se almacena en la siguiente ubicación dentro del pod: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt

y el token de cuenta de servicio predeterminado en: /var/run/secrets/kubernetes.io/serviceaccount/token

Puede utilizar el cliente godaddy de nodejs kubbernetes .

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);
}


3

Tuve un problema de autenticación similar en GKE donde los scripts de Python arrojaron excepciones repentinamente. La solución que funcionó para mí fue otorgar permiso a las vainas a través del rol

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

para obtener más información, ingrese la descripción del enlace aquí




0
curl -v -cacert <path to>/ca.crt --cert <path to>/kubernetes-node.crt --key <path to>/kubernetes-node.key https://<ip:port>

Mi versión de k8s es 1.2.0, y en otras versiones se supone que también funciona ^ ^


Lo anterior es correcto si tiene webhooks o algún otro RBAC habilitado. Esto es especialmente cierto> 1.2 de k8s
doktoroblivion

0

This is from the Kubernetes en acción book.

Debes encargarte de la autenticación . El servidor API en sí mismo dice que no está autorizado para acceder a él, porque no sabe quién es usted. .

Para autenticarse, necesita un token de autenticación. Afortunadamente, el token se proporciona a través del token secreto predeterminado mencionado anteriormente y se almacena en el archivo de token en el volumen secreto.

Vas a usar el token para acceder al servidor API . Primero, cargue el token en una variable de entorno:

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

El token ahora se almacena en la variable de entorno TOKEN . Puede usarlo al enviar solicitudes al servidor 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"  
      ]
  }
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.