Comment accéder à l'API Kubernetes à partir d'un conteneur de pod?

119

J'étais capable de boucler

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

comme URL de base, mais dans kubernetes 0.18.0, cela me donne "non autorisé". La chose étrange est que si j'ai utilisé l'adresse IP externe de la machine API ( http://172.17.8.101:8080/api/v1beta3/namespaces/default/), cela fonctionne très bien.

Tslater
la source
Où exécutez-vous votre cluster (GCE, AWS, etc.) et utilisez-vous quel système d'exploitation de base (Debian, CoreOS, etc.)?
Robert Bailey
Vagrant / CoreOS ... je vais éventuellement le déplacer vers AWS / CoreOS
tslater
D'où viennent les variables $KUBERNETES_SERVICE_HOSTet $KUBERNETES_PORT_443_TCP_PORT?
ruediste
J'ai trouvé ce guide incroyable pour 101 sur les comptes de service, les rôles et les liaisons de rôles developer.ibm.com/recipes/tutorials/… . La dernière section détaille comment nous pouvons accéder au formulaire d'API k8 dans les pods.
viv

Réponses:

132

Dans la documentation officielle, j'ai trouvé ceci:

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

Apparemment, il me manquait un jeton de sécurité dont je n'avais pas besoin dans une version précédente de Kubernetes. À partir de là, j'ai conçu ce que je pense être une solution plus simple que d'exécuter un proxy ou d'installer golang sur mon conteneur. Voir cet exemple qui récupère les informations, à partir de l'API, pour le conteneur actuel:

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

J'utilise également include un simple binaire, jq ( http://stedolan.github.io/jq/download/ ), pour analyser le json à utiliser dans les scripts bash.

Tslater
la source
5
Pour les clusters récemment déployés, vous voudrez peut-être changer v1beta3pourv1
Eyal Levin
6
Notez que cette commande curl se connectera de manière non sécurisée à l'apiserver (permettant à un homme du milieu d'intercepter le jeton du porteur), vous ne devriez donc l'utiliser que si le réseau entre le pod et l'apiserver est entièrement fiable. Sinon, vous devez passer le --cacertdrapeau à curl pour que curl valide le certificat présenté par l'apiserver.
Robert Bailey le
1
Je devais utiliser KUBERNETES_SERVICE_HOST=kubernetes.default, $KUBERNETES_443_TCP_PORT=443, NAMESPACE == $ (</ var / run / secrets / kubernetes.io / ServiceAccount / namespace) . The URL was kubernetes.default: 443 / api / v1 / namespaces / $ NAMESPACE / gousses / ... `. Notez que la version de l'API est définie sur v1 au lieu de v1beta3 et que l'espace de noms par défaut a été remplacé par $ NAMESPACE.
ruediste
74

Chaque pod a un compte de service automatiquement appliqué qui lui permet d'accéder à l'apiserver. Le compte de service fournit à la fois les informations d'identification du client, sous la forme d'un jeton de support, et le certificat d'autorité de certification qui a été utilisé pour signer le certificat présenté par l'apiserver. Avec ces deux informations, vous pouvez créer une connexion sécurisée et authentifiée à l'apisever sans utiliser curl -k(aka 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/
Robert Bailey
la source
2
Il convient de noter que pour que le cacert et le jeton existent tous les deux dans le compte de service, le contrôleur de réplication doit recevoir un --root-ca-file=argument lors du démarrage. (ceci est géré automatiquement dans la plupart des programmes d'installation de kubernetes). Voir la discussion ici pour plus de détails: github.com/kubernetes/kubernetes/issues/10265
JKnight
7
J'accédais au serveur API à partir d'un pod avec un espace de noms différent. J'ai donc dû l'utiliser https://kubernetes.default/comme hôte
ruediste
L'hôte officiel est kubernetes.default.svctel que documenté sur kubernetes.io/docs/tasks/access-application-cluster/…
Martin Tapp
17

Utilisation du client Python kubernetes.

from kubernetes import client, config

config.load_incluster_config()
v1_core = client.CoreV1Api()
rix
la source
1
Merci! Voici un petit repo avec un exemple, basé sur votre réponse, pour simplifier le jeu avec ce code.
Omer Levi Hevroni
10

version 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
Halil Kaskavalci
la source
6

L'addendum le plus important aux détails déjà mentionnés ci-dessus est que le pod à partir duquel vous essayez d'accéder au serveur API doit avoir les capacités RBAC pour le faire.

Chaque entité du système k8s est identifiée par un compte de service (comme le compte d'utilisateur utilisé pour les utilisateurs). En fonction des capacités RBAC, le jeton de compte de service (/var/run/secrets/kubernetes.io/serviceaccount/token) est renseigné. Les liaisons kube-api (par exemple pykube) peuvent prendre ce jeton comme entrée lors de la création de la connexion aux serveurs kube-api. Si le pod a les bonnes capacités RBAC, le pod pourra établir la connexion avec le serveur kube-api.

pr-pal
la source
5

J'ai rencontré ce problème en essayant d'accéder à l'API depuis l'intérieur d'un pod à l'aide de Go Code. Vous trouverez ci-dessous ce que j'ai mis en œuvre pour que cela fonctionne, si quelqu'un rencontre cette question qui souhaite également utiliser Go.

L'exemple utilise une ressource de pod, pour laquelle vous devez utiliser la client-gobibliothèque si vous travaillez avec des objets Kubernetes natifs. Le code est plus utile pour ceux qui travaillent avec 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
KyleHodgetts
la source
4

Depuis l'intérieur du pod, le serveur api kubernetes peut être accessible directement sur " https: //kubernetes.default ". Par défaut, il utilise le "compte de service par défaut" pour accéder au serveur api.

Ainsi, nous devons également transmettre un «certificat ca» et un «jeton de compte de service par défaut» pour nous authentifier auprès du serveur api.

Le fichier de certificat est stocké à l'emplacement suivant dans le pod: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt

et le jeton de compte de service par défaut à: /var/run/secrets/kubernetes.io/serviceaccount/token

Vous pouvez utiliser le client godaddy 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);
}

Utkarsh Yeolekar
la source
3

J'ai eu un problème d'authentification similaire sur GKE où les scripts python ont soudainement jeté des exceptions. La solution qui a fonctionné pour moi était de donner la permission aux pods via le rôle

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

pour plus d'informations, entrez la description du lien ici

Canard en caoutchouc
la source
2

Pour quiconque utilise Google Container Engine (optimisé par Kubernetes):

Un simple appel https://kubernetesdepuis l'intérieur du cluster à l'aide de ce client kubernetes pour Java fonctionne.

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

Ma version k8s est 1.2.0, et dans d'autres versions, elle est censée fonctionner aussi ^^

Matin Y
la source
Ce qui précède est correct si vous avez activé les webhooks ou un autre RBAC. Ceci est particulièrement vrai> 1,2 de k8s
doktoroblivion
0

This is from the Kubernetes en action book.

Vous devez vous occuper de l' authentification . Le serveur API lui-même dit que vous n'êtes pas autorisé à y accéder, car il ne sait pas qui vous êtes .

Pour vous authentifier, vous avez besoin d'un jeton d'authentification. Heureusement, le jeton est fourni via le jeton secret par défaut mentionné précédemment et est stocké dans le fichier de jeton dans le volume secret.

Vous allez utiliser le jeton pour accéder au serveur API . Tout d'abord, chargez le jeton dans une variable d'environnement:

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

Le jeton est maintenant stocké dans la variable d' environnement TOKEN . Vous pouvez l'utiliser lors de l'envoi de requêtes au serveur 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"  
      ]
  }
fgul
la source