Как получить доступ к API Kubernetes из контейнера pod?


119

Раньше я могла завить

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

как мой базовый URL, но в Kubernetes 0.18.0 он дает мне "неавторизованный". Странно то, что если бы я использовал внешний IP-адрес машины API ( http://172.17.8.101:8080/api/v1beta3/namespaces/default/), он работал нормально.


Где вы запускаете свой кластер (GCE, AWS и т. Д.) И какую базовую ОС (debian, CoreOS и т. Д.)?
Роберт Бейли

Vagrant / CoreOS ... я в конечном итоге перенесу его на AWS / CoreOS
позже

Откуда же $KUBERNETES_SERVICE_HOSTи $KUBERNETES_PORT_443_TCP_PORTпеременные берутся?
ruediste

Я нашел это руководство , чтобы быть удивительным для 101 на служебных счетах, роли и rolebindings developer.ibm.com/recipes/tutorials/... . В последнем разделе подробно описано, как мы можем получить доступ к форме API k8 внутри модулей.
viv

Ответы:


132

В официальной документации я нашел это:

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

По-видимому, мне не хватало токена безопасности, который мне не понадобился в предыдущей версии Kubernetes. Исходя из этого, я разработал то, что я считаю более простым решением, чем запуск прокси или установка golang в моем контейнере. См. Этот пример, который получает информацию из API для текущего контейнера:

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

Я также использую простой двоичный файл jq ( http://stedolan.github.io/jq/download/ ) для синтаксического анализа json для использования в сценариях bash.


5
Для недавно развернутых кластеров вы можете захотеть перейти v1beta3наv1
Эяль Левин

6
Обратите внимание, что эта команда curl будет небезопасно подключаться к apiserver (что позволяет человеку посередине перехватить токен-носитель), поэтому вы должны использовать ее только в том случае, если сеть между модулем и apiserver полностью доверена. В противном случае вы должны передать --cacertфлаг curl, чтобы curl проверял сертификат, представленный apiserver.
Роберт Бейли

1
Я должен был использовать KUBERNETES_SERVICE_HOST=kubernetes.default, $KUBERNETES_443_TCP_PORT=443, NAMESPACE == $ (</ вар / бег / секреты / kubernetes.io / ServiceAccount / пространство имен) . The URL was kubernetes.default: 443 / API / v1 / пространство имен / $ NAMESPACE / стручки / ... `. Обратите внимание, что версия API установлена ​​на v1 вместо v1beta3, а пространство имен по умолчанию было заменено на $ NAMESPACE.
ruediste

74

К каждому модулю автоматически применяется учетная запись службы, которая позволяет ему получить доступ к apiserver. Учетная запись службы предоставляет как учетные данные клиента в виде токена-носителя, так и сертификат центра сертификации, который использовался для подписи сертификата, представленного apiserver. С помощью этих двух частей информации вы можете создать безопасное аутентифицированное соединение с apisever без использования curl -k(иначе 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
Следует отметить, что для того, чтобы и cacert, и токен существовали в учетной записи службы, контроллеру репликации должен быть задан --root-ca-file=аргумент при запуске. (это выполняется автоматически в большинстве установщиков Kubernetes). Подробнее см. Обсуждение здесь: github.com/kubernetes/kubernetes/issues/10265
JKnight,

7
Я обращался к серверу API из модуля с другим пространством имен. Таким образом, мне пришлось использовать в https://kubernetes.default/качестве ведущего
ruediste

Официальный хост, kubernetes.default.svcкак указано
Мартин Тэпп,

17

Использование клиента Python kubernetes ..

from kubernetes import client, config

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

1
Спасибо! Вот небольшое репо с примером, основанным на вашем ответе, чтобы упростить работу с этим кодом.
Омер Леви Хеврони

10

версия 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

Наиболее важным дополнением к уже упомянутым выше деталям является то, что модуль, из которого вы пытаетесь получить доступ к серверу API, должен иметь для этого возможности RBAC.

Каждый объект в системе k8s идентифицируется служебной учетной записью (например, учетная запись пользователя, используемая для пользователей). В зависимости от возможностей RBAC заполняется токен учетной записи службы (/var/run/secrets/kubernetes.io/serviceaccount/token). Привязки kube-api (например, pykube) могут принимать этот токен в качестве входных данных при создании подключения к kube-api-серверам. Если модуль имеет правильные возможности RBAC, модуль сможет установить соединение с сервером kube-api.


5

Я столкнулся с этой проблемой при попытке получить доступ к API из модуля с помощью Go Code. Ниже показано, что я реализовал, чтобы это работало, если кто-то столкнется с этим вопросом, желая также использовать Go.

В примере используется pod-ресурс, для которого вы должны использовать client-goбиблиотеку, если вы работаете с собственными объектами kubernetes. Код более полезен для тех, кто работает с 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

Изнутри модуля api-сервер kubernetes может быть доступен прямо по адресу « https: //kubernetes.default ». По умолчанию он использует «служебную учетную запись по умолчанию» для доступа к серверу api.

Таким образом, нам также необходимо передать «сертификат CA» и «токен учетной записи службы по умолчанию» для аутентификации на сервере api.

файл сертификата хранится в следующем месте внутри модуля: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt

и токен учетной записи службы по умолчанию: /var/run/secrets/kubernetes.io/serviceaccount/token

Вы можете использовать клиент 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);
}


3

У меня была аналогичная проблема с авторизацией в GKE, когда скрипты python внезапно вызывали исключения. Решение, которое сработало для меня, заключалось в том, чтобы дать стручкам разрешение через роль

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

для получения дополнительной информации введите описание ссылки здесь



2

При включенном RBAC учетная запись службы по умолчанию не имеет разрешений.

Лучше создать отдельную учетную запись службы для своих нужд и использовать ее для создания своего модуля.

spec:
  serviceAccountName: secret-access-sa
  containers:
    ...

Здесь хорошо объяснено https://developer.ibm.com/recipes/tutorials/service-accounts-and-auditing-in-kubernetes/


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

Моя версия k8s - 1.2.0, и в других версиях она тоже должна работать ^ ^


Вышеуказанное верно, если у вас включены веб-перехватчики или какой-либо другой RBAC. Особенно это актуально> 1.2 из k8s
doktoroblivion

0

This is from the Kubernetes в действии book.

Вам нужно позаботиться об аутентификации . Сам сервер API сообщает, что у вас нет доступа к нему, потому что он не знает, кто вы .

Для аутентификации вам понадобится токен аутентификации. К счастью, токен предоставляется через секретный токен по умолчанию, упомянутый ранее, и хранится в файле токена в секретном томе.

Вы собираетесь использовать токен для доступа к серверу API . Сначала загрузите токен в переменную окружения:

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

Токен теперь хранится в переменной среды TOKEN . Вы можете использовать его при отправке запросов на сервер 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"  
      ]
  }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.