Como acesso a API Kubernetes de dentro de um contêiner de pod?

118

Eu costumava ser capaz de enrolar

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

como meu URL base, mas no kubernetes 0.18.0 ele me dá "não autorizado". O estranho é que se eu usar o endereço IP externo da máquina API ( http://172.17.8.101:8080/api/v1beta3/namespaces/default/), ele funciona perfeitamente.

tslater
fonte
Onde você está executando seu cluster (GCE, AWS, etc) e usando qual sistema operacional base (debian, CoreOS, etc)?
Robert Bailey
Vagrant / CoreOS ... eventualmente, irei movê-lo para AWS / CoreOS
tslater
De onde vêm as variáveis $KUBERNETES_SERVICE_HOSTe $KUBERNETES_PORT_443_TCP_PORT?
ruediste
Achei este guia incrível para 101 sobre contas de serviço, funções e rolebindings developer.ibm.com/recipes/tutorials/… . A última seção detalha como podemos acessar o formulário API k8 dentro dos pods.
viv

Respostas:

132

Na documentação oficial encontrei o seguinte:

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

Aparentemente, estava faltando um token de segurança que não precisava em uma versão anterior do Kubernetes. A partir disso, desenvolvi o que considero uma solução mais simples do que executar um proxy ou instalar o golang no meu contêiner. Veja este exemplo que obtém as informações, da api, para o contêiner atual:

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

Eu também uso include um binário simples, jq ( http://stedolan.github.io/jq/download/ ), para analisar o json para uso em scripts bash.

tslater
fonte
5
Para clusters implantados recentemente, você pode querer mudar v1beta3parav1
Eyal Levin
6
Observe que este comando curl se conectará de forma insegura ao apiserver (possibilitando que um man-in-the-middle interceptar o token do portador), portanto, você só deve usá-lo se a rede entre o pod e o apiserver for totalmente confiável. Caso contrário, você deve passar o --cacertsinalizador para curl para que curl valide o certificado apresentado pelo apiserver.
Robert Bailey
1
Eu tive que usar KUBERNETES_SERVICE_HOST=kubernetes.default, $KUBERNETES_443_TCP_PORT=443, NAMESPACE == $ (</ var / run / segredos / kubernetes.io / ServiceAccount / namespace) . The URL was kubernetes.default: 443 / api / v1 / namespaces / $ NAMESPACE / vagens / ... `. Observe que a versão da API é definida como v1 em vez de v1beta3 e o namespace padrão foi substituído por $ NAMESPACE.
ruediste
74

Cada pod tem uma conta de serviço automaticamente aplicada que permite acessar o apiserver. A conta de serviço fornece credenciais de cliente, na forma de um token de portador, e o certificado da autoridade de certificação que foi usado para assinar o certificado apresentado pelo apiserver. Com essas duas informações, você pode criar uma conexão segura e autenticada com o apisever sem usar curl -k(também conhecido 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/
Robert Bailey
fonte
2
Deve-se observar que, para que o cacert e o token existam na conta de serviço, o controlador de replicação deve receber um --root-ca-file=argumento quando iniciado. (isso é tratado automaticamente na maioria dos instaladores do Kubernetes). Veja a discussão aqui para mais detalhes: github.com/kubernetes/kubernetes/issues/10265
JKnight
7
Eu estava acessando o servidor API de um pod com um namespace diferente. Então eu tive que usar https://kubernetes.default/como host
ruediste
O host oficial está kubernetes.default.svcdocumentado em kubernetes.io/docs/tasks/access-application-cluster/…
Martin Tapp
17

Usando o cliente Python kubernetes ..

from kubernetes import client, config

config.load_incluster_config()
v1_core = client.CoreV1Api()
Rix
fonte
1
Obrigado! Aqui está um pequeno repositório com um exemplo, com base em sua resposta, para tornar mais simples brincar com este código.
Omer Levi Hevroni
10

versão 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
fonte
6

O adendo mais importante aos detalhes já mencionados acima é que o pod do qual você está tentando acessar o servidor API deve ter os recursos RBAC para fazer isso.

Cada entidade no sistema k8s é identificada por uma conta de serviço (como uma conta de usuário usada para usuários). Com base nos recursos do RBAC, o token da conta de serviço (/var/run/secrets/kubernetes.io/serviceaccount/token) é preenchido. As ligações kube-api (por exemplo, pykube) podem usar esse token como uma entrada ao criar conexão com os servidores kube-api. Se o pod tiver os recursos de RBAC corretos, ele será capaz de estabelecer a conexão com o servidor kube-api.

pr-pal
fonte
5

Encontrei esse problema ao tentar acessar a API de dentro de um pod usando o código Go. Abaixo está o que eu implementei para fazer isso funcionar, caso alguém se depare com essa pergunta e queira usar o Go também.

O exemplo usa um recurso de pod, para o qual você deve usar a client-gobiblioteca se estiver trabalhando com objetos kubernetes nativos. O código é mais útil para quem trabalha com 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
fonte
4

De dentro do pod, o servidor kubernetes api pode ser acessado diretamente em " https: //kubernetes.default ". Por padrão, ele usa a "conta de serviço padrão" para acessar o servidor API.

Portanto, também precisamos passar um "ca cert" e um "token de conta de serviço padrão" para autenticar com o servidor API.

arquivo de certificado é armazenado no seguinte local dentro do pod: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt

e o token de conta de serviço padrão em: /var/run/secrets/kubernetes.io/serviceaccount/token

Você pode usar o cliente nodejs kubbernetes godaddy .

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

Tive um problema de autenticação semelhante no GKE, em que scripts python de repente lançaram exceções. A solução que funcionou para mim foi dar permissão aos pods por meio de função

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 mais informações insira a descrição do link aqui

Pato de borracha
fonte
2

Para quem está usando o Google Container Engine (com tecnologia Kubernetes):

Uma chamada simples https://kubernetesde dentro do cluster usando este cliente kubernetes para Java funciona.

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

Minha versão do k8s é 1.2.0 e em outras versões deve funcionar também ^ ^

Manhã Y
fonte
O acima está correto se você tiver webhooks ou algum outro RBAC habilitado. Isso é especialmente verdadeiro> 1,2 de k8s
doktoroblivion
0

This is from the Kubernetes em ação book.

Você precisa cuidar da autenticação . O próprio servidor API diz que você não está autorizado a acessá-lo, porque não sabe quem você é .

Para autenticar, você precisa de um token de autenticação. Felizmente, o token é fornecido por meio do segredo de token padrão mencionado anteriormente e é armazenado no arquivo de token no volume do segredo.

Você vai usar o token para acessar o servidor API . Primeiro, carregue o token em uma variável de ambiente:

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

O token agora está armazenado na variável de ambiente TOKEN . Você pode usá-lo ao enviar solicitações ao servidor de 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
fonte