Come si accede all'API Kubernetes da un contenitore pod?


118

Ero in grado di arricciarmi

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

come URL di base, ma in kubernetes 0.18.0 mi dà "non autorizzato". La cosa strana è che se ho usato l'indirizzo IP esterno della macchina API ( http://172.17.8.101:8080/api/v1beta3/namespaces/default/), funziona perfettamente.


Dove stai eseguendo il tuo cluster (GCE, AWS, ecc.) E utilizzando quale sistema operativo di base (Debian, CoreOS, ecc.)?
Robert Bailey

Vagrant / CoreOS ... alla fine lo sposterò in AWS / CoreOS
tslater

Da dove vengono le variabili $KUBERNETES_SERVICE_HOSTe $KUBERNETES_PORT_443_TCP_PORT?
ruediste

Ho trovato questa guida straordinaria per 101 su account di servizio, ruoli e associazioni di ruoli developer.ibm.com/recipes/tutorials/… . L'ultima sezione descrive in dettaglio come possiamo accedere al modulo API k8 all'interno dei pod.
viv

Risposte:


132

Nella documentazione ufficiale ho trovato questo:

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

Apparentemente mi mancava un token di sicurezza di cui non avevo bisogno in una versione precedente di Kubernetes. Da ciò, ho ideato quella che penso sia una soluzione più semplice rispetto all'esecuzione di un proxy o all'installazione di golang sul mio contenitore. Vedi questo esempio che ottiene le informazioni, dall'API, per il contenitore corrente:

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

Uso anche include un semplice binario, jq ​​( http://stedolan.github.io/jq/download/ ), per analizzare il json da utilizzare negli script bash.


5
Per i cluster distribuiti di recente potresti voler passare v1beta3av1
Eyal Levin

6
Nota che questo comando curl si connetterà in modo insicuro all'apiserver (rendendo possibile a un uomo-in-the-middle di intercettare il token portante), quindi dovresti usarlo solo se la rete tra il pod e l'apiserver è completamente attendibile. Altrimenti, dovresti passare il --cacertflag a curl in modo che curl convalidi il certificato presentato dall'apiserver.
Robert Bailey

1
Ho dovuto usare KUBERNETES_SERVICE_HOST=kubernetes.default, $KUBERNETES_443_TCP_PORT=443, NAMESPACE == $ (</ var / run / segreti / kubernetes.io / serviceaccount / namespace) . The URL was kubernetes.default: 443 / api / v1 / namespace / $ NAMESPACE / cialde / ... `. Tieni presente che la versione API è impostata su v1 anziché su v1beta3 e lo spazio dei nomi predefinito è stato sostituito con $ NAMESPACE.
ruediste

74

Ogni pod ha un account di servizio applicato automaticamente che gli consente di accedere all'apiserver. L'account del servizio fornisce sia le credenziali del client, sotto forma di token di portatore, sia il certificato dell'autorità di certificazione utilizzato per firmare il certificato presentato dall'apiserver. Con queste due informazioni, puoi creare una connessione sicura e autenticata con l'apisever senza utilizzare 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/

2
Va notato che affinché cacert e token esistano entrambi nell'account di servizio, al controller di replica deve essere assegnato un --root-ca-file=argomento all'avvio. (questo viene gestito automaticamente nella maggior parte dei programmi di installazione di kubernetes). Vedere la discussione qui per maggiori dettagli: github.com/kubernetes/kubernetes/issues/10265
JKnight

7
Stavo accedendo al server API da un pod con uno spazio dei nomi diverso. Così ho dovuto usare https://kubernetes.default/come host
ruediste

L'host ufficiale è kubernetes.default.svcdocumentato su kubernetes.io/docs/tasks/access-application-cluster/…
Martin Tapp

17

Utilizzo del client Kubernetes di Python ..

from kubernetes import client, config

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

1
Grazie! Ecco un piccolo repo con un esempio, basato sulla tua risposta, per rendere più semplice giocare con questo codice.
Omer Levi Hevroni,

10

versione 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

L'aggiunta più importante ai dettagli già menzionati sopra è che il pod da cui stai tentando di accedere al server API dovrebbe avere le capacità RBAC per farlo.

Ogni entità nel sistema k8s è identificata da un account di servizio (come l'account utente utilizzato per gli utenti). In base alle funzionalità RBAC, il token dell'account di servizio (/var/run/secrets/kubernetes.io/serviceaccount/token) viene popolato. I collegamenti kube-api (ad esempio pykube) possono prendere questo token come input durante la creazione della connessione ai server kube-api. Se il pod ha le capacità RBAC corrette, il pod sarà in grado di stabilire la connessione con il server kube-api.


5

Ho riscontrato questo problema durante il tentativo di accedere all'API dall'interno di un pod utilizzando Go Code. Di seguito è riportato ciò che ho implementato per farlo funzionare, se qualcuno dovesse incontrare questa domanda che desidera utilizzare anche Go.

L'esempio utilizza una risorsa pod, per la quale dovresti usare la client-golibreria se stai lavorando con oggetti kubernetes nativi. Il codice è più utile per coloro che lavorano 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

Dall'interno del pod, il server api kubernetes può essere accessibile direttamente su " https: //kubernetes.default ". Per impostazione predefinita, utilizza l '"account di servizio predefinito" per accedere al server API.

Quindi, dobbiamo anche passare un "certificato ca" e un "token dell'account di servizio predefinito" per autenticarci con il server API.

il file del certificato è archiviato nella seguente posizione all'interno del pod: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt

e il token dell'account di servizio predefinito su: /var/run/secrets/kubernetes.io/serviceaccount/token

È possibile utilizzare il client godaddy di 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

Ho avuto un problema di autenticazione simile su GKE in cui gli script Python hanno improvvisamente generato eccezioni. La soluzione che ha funzionato per me è stata quella di dare il permesso ai pod attraverso il ruolo

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

per maggiori informazioni inserire qui la descrizione del collegamento




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

La mia versione k8s è 1.2.0, e anche in altre versioni dovrebbe funzionare ^ ^


Quanto sopra è corretto se hai webhook o qualche altro RBAC abilitato. Ciò è particolarmente vero> 1.2 di k8s
doktoroblivion

0

This is from the Kubernetes in azione book.

Devi occuparti dell'autenticazione . Il server API stesso dice che non sei autorizzato ad accedervi, perché non sa chi sei .

Per l'autenticazione, è necessario un token di autenticazione. Fortunatamente, il token viene fornito tramite il token segreto predefinito menzionato in precedenza e viene archiviato nel file token nel volume segreto.

Utilizzerai il token per accedere al server API . Innanzitutto, carica il token in una variabile di ambiente:

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

Il token è ora archiviato nella variabile d' ambiente TOKEN . Puoi usarlo quando invii richieste al server 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"  
      ]
  }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.