Wie greife ich von einem Pod-Container aus auf die Kubernetes-API zu?

117

Früher konnte ich mich kräuseln

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

als meine Basis-URL, aber in kubernetes 0.18.0 gibt es mir "nicht autorisiert". Das Seltsame ist, dass wenn ich die externe IP-Adresse des API-Rechners ( http://172.17.8.101:8080/api/v1beta3/namespaces/default/) verwende, es gut funktioniert.

später
quelle
Wo führen Sie Ihren Cluster aus (GCE, AWS usw.) und verwenden welches Basisbetriebssystem (Debian, CoreOS usw.)?
Robert Bailey
Vagrant / CoreOS ... ich werde es schließlich zu AWS / CoreOS verschieben
tslater
Wo kommen die $KUBERNETES_SERVICE_HOSTund $KUBERNETES_PORT_443_TCP_PORTkommen Variablen aus?
ruediste
Ich fand diesen Leitfaden für 101 in Bezug auf Dienstkonten, Rollen und Rollenbindungen erstaunlich. Developer.ibm.com/recipes/tutorials/… . Der letzte Abschnitt beschreibt, wie wir innerhalb der Pods auf das k8-API-Formular zugreifen können.
Viv

Antworten:

131

In der offiziellen Dokumentation fand ich Folgendes:

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

Anscheinend fehlte mir ein Sicherheitstoken, das ich in einer früheren Version von Kubernetes nicht benötigte. Daraus entwickelte ich eine meiner Meinung nach einfachere Lösung als das Ausführen eines Proxys oder das Installieren von Golang auf meinem Container. In diesem Beispiel werden die Informationen für den aktuellen Container von der API abgerufen:

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

Ich verwende auch eine einfache Binärdatei, jq ( http://stedolan.github.io/jq/download/ ), um den json für die Verwendung in Bash-Skripten zu analysieren.

später
quelle
5
Für kürzlich bereitgestellte Cluster möchten Sie möglicherweise v1beta3zuv1
Eyal Levin
6
Beachten Sie, dass dieser Curl-Befehl eine unsichere Verbindung zum Apiserver herstellt (wodurch ein Mann in der Mitte das Bearer-Token abfangen kann). Sie sollten ihn daher nur verwenden, wenn das Netzwerk zwischen dem Pod und dem Apiserver vollständig vertrauenswürdig ist. Andernfalls sollten Sie das --cacertFlag an curl übergeben, damit curl das vom Apiserver vorgelegte Zertifikat validiert.
Robert Bailey
1
Ich hatte zu verwenden KUBERNETES_SERVICE_HOST=kubernetes.default, $KUBERNETES_443_TCP_PORT=443Namespaces == $ (</ var / run / Geheimnisse / kubernetes.io / serviceaccount / Namespace) . The URL was kubernetes.default: 443 / api / v1 / Namespaces / $ namespace / Hülsen / ... `. Beachten Sie, dass die API-Version auf v1 anstelle von v1beta3 festgelegt ist und der Standard-Namespace durch $ NAMESPACE ersetzt wurde.
ruediste
74

Auf jeden Pod wird automatisch ein Dienstkonto angewendet, mit dem er auf den Apiserver zugreifen kann. Das Dienstkonto stellt sowohl Client-Anmeldeinformationen in Form eines Inhaber-Tokens als auch das Zertifikat der Zertifizierungsstelle bereit, mit dem das vom Apiserver vorgelegte Zertifikat signiert wurde. Mit diesen beiden Informationen können Sie eine sichere, authentifizierte Verbindung zum Apisever herstellen, ohne curl -k(aka curl --insecure) zu verwenden:

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
quelle
2
Es ist zu beachten, dass der Replikationscontroller --root-ca-file=beim Start ein Argument erhalten muss, damit sowohl das Cacert als auch das Token im Dienstkonto vorhanden sind . (Dies wird in den meisten Kubernetes-Installationsprogrammen automatisch behandelt.) Siehe Diskussion hier für weitere Details: github.com/kubernetes/kubernetes/issues/10265
JKnight
7
Ich habe von einem Pod mit einem anderen Namespace auf den API-Server zugegriffen. Daher musste ich https://kubernetes.default/als Host
ruediste
Offizieller Host ist kubernetes.default.svcwie unter kubernetes.io/docs/tasks/access-application-cluster/…
Martin Tapp
17

Verwenden des Python-Kubernetes-Clients.

from kubernetes import client, config

config.load_incluster_config()
v1_core = client.CoreV1Api()
rix
quelle
1
Vielen Dank! Hier ist ein kleines Repo mit einem Beispiel, das auf Ihrer Antwort basiert, um das Spielen mit diesem Code zu vereinfachen.
Omer Levi Hevroni
10

wget version:

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
quelle
6

Der wichtigste Zusatz zu den oben bereits erwähnten Details ist, dass der Pod, von dem aus Sie auf den API-Server zugreifen möchten, über die RBAC-Funktionen verfügen sollte.

Jede Entität im k8s-System wird durch ein Dienstkonto identifiziert (wie das Benutzerkonto, das für Benutzer verwendet wird). Basierend auf den RBAC-Funktionen wird das Dienstkontotoken (/var/run/secrets/kubernetes.io/serviceaccount/token) ausgefüllt. Die kube-api-Bindungen (z. B. pykube) können dieses Token als Eingabe verwenden, wenn eine Verbindung zu den kube-api-Servern hergestellt wird. Wenn der Pod über die richtigen RBAC-Funktionen verfügt, kann der Pod die Verbindung zum kube-api-Server herstellen.

pr-pal
quelle
5

Ich bin auf dieses Problem gestoßen, als ich versucht habe, mit Go Code aus einem Pod heraus auf die API zuzugreifen. Im Folgenden ist aufgeführt, was ich implementiert habe, damit dies funktioniert, falls jemand auf diese Frage stößt und auch Go verwenden möchte.

In diesem Beispiel wird eine Pod-Ressource verwendet, für die Sie die client-goBibliothek verwenden sollten, wenn Sie mit nativen Kubernetes-Objekten arbeiten. Der Code ist hilfreicher für Benutzer von 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
quelle
4

Auf den Kubernetes-API-Server kann innerhalb des Pods direkt unter " https: //kubernetes.default " zugegriffen werden . Standardmäßig wird das "Standarddienstkonto" für den Zugriff auf den API-Server verwendet.

Daher müssen wir auch ein "ca cert" und ein "default service account token" übergeben, um sich beim API-Server zu authentifizieren.

Die Zertifikatdatei wird an der folgenden Stelle im Pod gespeichert: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt

und das Standard-Dienstkonto-Token unter: /var/run/secrets/kubernetes.io/serviceaccount/token

Sie können den nodejs kubbernetes godaddy client verwenden .

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
quelle
3

Ich hatte ein ähnliches Authentifizierungsproblem bei GKE, bei dem Python-Skripte plötzlich Ausnahmen auslösten. Die Lösung, die für mich funktioniert hat, bestand darin, den Pods die Erlaubnis durch die Rolle zu geben

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

Für weitere Informationen geben Sie hier die Linkbeschreibung ein

Badeente
quelle
2

Für alle, die Google Container Engine (unterstützt von Kubernetes) verwenden:

Ein einfacher Aufruf https://kubernetesaus dem Cluster mit diesem kubernetes-Client für Java funktioniert.

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

Meine k8s-Version ist 1.2.0 und in anderen Versionen soll es auch funktionieren ^^

Morgen Y.
quelle
Das Obige ist korrekt, wenn Sie Webhooks oder einen anderen RBAC aktiviert haben. Dies gilt insbesondere> 1.2 von k8s
doktoroblivion
0

This is from the Kubernetes in Aktion book.

Sie müssen sich um die Authentifizierung kümmern . Der API-Server selbst gibt an, dass Sie nicht berechtigt sind, darauf zuzugreifen, da er nicht weiß, wer Sie sind .

Zur Authentifizierung benötigen Sie ein Authentifizierungstoken. Glücklicherweise wird das Token über das Standard-Token-Geheimnis bereitgestellt zuvor erwähnte und in der Token-Datei auf dem geheimen Volume gespeichert.

Sie werden das Token verwenden, um auf den API-Server zuzugreifen . Laden Sie zunächst das Token in eine Umgebungsvariable:

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

Das Token wird jetzt in der gespeicherten TOKEN Umgebung variabel . Sie können es verwenden, wenn Sie Anforderungen an den API-Server senden:

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
quelle