Для улучшения работы сайта мы используем файлы cookies. Оставаясь на сайте, вы соглашаетесь с политикой обработки персональных данных.

Методы аутентификации. Kubernetes

Данный механизм может использовать внешние сертификаты X.509 как часть проверки TLS или подписи. Проверка подписей по сертификатам X.509, использующим SHA-1, является устаревшей и больше не используется без обходного пути. Дополнительную информацию см. в разделе FAQ об устаревании.

Метод аутентификации kubernetes можно использовать для аутентификации в StarVault с помощью токена учетной записи сервиса Kubernetes. Этот метод аутентификации позволяет легко внедрить токен StarVault в под Kubernetes.

Также можете использовать токен учетной записи сервиса Kubernetes для входа в систему через JWT-аутентификацию. В разделе "Как работать с недолговечными токенами Kubernetes" кратко описана работа с недолговечными токенами Kubernetes, и как сравнить JWT-аутентификацию с аутентификацией Kubernetes.

Если вы обновляетесь до Kubernetes v1.21+, убедитесь, что параметр конфигурации disable_iss_validation установлен в true. Предполагая путь монтирования по умолчанию, вы можете проверить это с помощью команды starvault read -field disable_iss_validation auth/kubernetes/config. Дополнительные сведения см. в разделе Kubernetes 1.21 ниже.

1. Аутентификация

1.1. С помощью CLI

По умолчанию используется путь /kubernetes. Если этот метод авторизации был включен по другому пути, укажите в CLI -path=/my-path, где my-path — путь, по которому выключен метод аутентификации.

starvault write auth/kubernetes/login role=demo jwt=...

1.2. С помощью API

По умолчанию, конечная точка — auth/kubernetes/login. Если этот метод авторизации был включен по другому пути, используйте это значение вместо kubernetes.

curl \
    --request POST \
    --data '{"jwt": "<your service account jwt>", "role": "demo"}' \
    http://127.0.0.1:8200/v1/auth/kubernetes/login

В ответе будет содержаться токен auth.client_token:

{
  "auth": {
    "client_token": "38fe9691-e623-7238-f618-c94d4e7bc674",
    "accessor": "78e87a38-84ed-2692-538f-ca8b9f400ab3",
    "policies": ["default"],
    "metadata": {
      "role": "demo",
      "service_account_name": "myapp",
      "service_account_namespace": "default",
      "service_account_secret_name": "myapp-token-pd21c",
      "service_account_uid": "aa9aa8ff-98d0-11e7-9bb7-0800276d99bf"
    },
    "lease_duration": 2764800,
    "renewable": true
  }
}

2. Конфигурация

Методы аутентификации должны быть настроены заранее, прежде чем пользователи или машины смогут пройти аутентификацию. Эти шаги обычно выполняются оператором или инструментом управления конфигурацией.

  1. Включите метод аутентификации Kubernetes:

    starvault auth enable kubernetes
  2. Используйте конечную точку /config, чтобы настроить StarVault на взаимодействие с Kubernetes. Используйте kubectl cluster-info для проверки адреса хоста и TCP-порта Kubernetes.

    starvault write auth/kubernetes/config \
        token_reviewer_jwt="<your reviewer service account JWT>" \
        kubernetes_host=https://192.168.99.100:<your TCP port or blank for 443> \
        kubernetes_ca_cert=@ca.crt

    Шаблон, который StarVault использует для аутентификации подов, зависит от обмена JWT-токеном по сети. Учитывая модель безопасности StarVault, это допустимо, поскольку StarVault является частью базы доверенных вычислений. В целом, приложения Kubernetes не должны передавать этот JWT другим приложениям, поскольку позволяет выполнять вызовы API от имени пода и может привести к непреднамеренному предоставлению доступа третьим лицам.

  3. Создайте именованную роль:

    starvault write auth/kubernetes/role/demo \
        bound_service_account_names=myapp \
        bound_service_account_namespaces=default \
        policies=default \
        ttl=1h

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

3. Kubernetes 1.21

Начиная с версии 1.21, функция Kubernetes BoundServiceAccountTokenVolume по умолчанию включена. Это изменяет JWT-токен, устанавливаемый в контейнеры по умолчанию, двумя способами, важными для аутентификации Kubernetes :

  • Имеет срок действия и привязан к сроку жизни пода и учетной записи сервиса.

  • Значение параметра "iss" в JWT зависит от конфигурации кластера.

Изменения в сроке жизни токена важны при настройке опции token_reviewer_jwt. Если используется токен с коротким сроком действия, Kubernetes отзовет его, как только под или учетная запись сервиса будут удалены, или если истечет срок действия, и StarVault больше не сможет использовать API TokenReview. Подробнее о том, как работать с недолговечными токенами Kubernetes, читайте ниже.

В ответ на изменения эмитента в StarVault был обновлен метод аутентификации Kubernetes, который по умолчанию не проверяет эмитент. API Kubernetes выполняет ту же проверку при просмотре токенов, поэтому включение проверки эмитента на стороне StarVault — дублирование работы. Без отключения проверки эмитента StarVault невозможно, чтобы одна конфигурация аутентификации Kubernetes работала для установленных по умолчанию токенов подов в Kubernetes 1.20 и в 1.21. Если хотите включить проверку эмитента в StarVault, смотрите раздел Обнаружение issuer учетной записи службы ниже.

3.1. Как работать с недолговечными токенами kubernetes

Существует несколько способов настройки аутентификации для подов Kubernetes, когда установленные по умолчанию токены подов недолговечны, каждый из которых имеет свои преимущества. В этой таблице представлены все варианты, каждый из которых более подробно описан ниже.

Опция Все токены недолговечны Можно досрочно отозвать токены Другие соображения

Используйте локальный токен в качестве JWT-ревьюера

Да

Да

Требуется развертывание StarVault на кластере Kubernetes

Используйте JWT клиента в качестве JWT-ревьюера

Да

Да

Операционные накладные расходы

Использование долгоживущего токена в качестве JWT-ревьюера

Нет

Да

Вместо этого используйте JWT-авторизацию

Да

Нет

По умолчанию Kubernetes продлевает срок действия внедренных токенов учетных записей сервисов до года, чтобы сгладить переход к недолговечным токенам. Если хотите отключить эту функцию, задайте --service-account-extend-token-expiration=false для kube-apiserver или укажите свой собственный монтируемый том serviceAccountToken. Пример смотрите здесь.

3.1.1. Используйте токен учетной записи локальной службы в качестве JWT-ревьюера

При запуске StarVault в поде Kubernetes рекомендуется использовать токен локальной учетной записи службы пода. StarVault будет периодически перечитывать файл, чтобы поддерживать недолговечные токены. Чтобы использовать локальный токен и сертификат центра сертификации, опустите token_reviewer_jwt и kubernetes_ca_cert при настройке метода аутентификации. StarVault попытается загрузить их из файлов token и ca.crt соответственно в папке монтирования по умолчанию /var/run/secrets/kubernetes.io/serviceaccount/.

starvault write auth/kubernetes/config \
    kubernetes_host=https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT

Когда срок действия токена учетной записи сервиса истечет или он будет отозван, StarVault больше не сможет использовать API TokenReview, и аутентификация клиента завершится неудачей.

3.1.2. Используйте JWT клиента StarVault в качестве JWT-ревьюера.

При настройке Kubernetes auth можно опустить параметр token_reviewer_jwt, и StarVault будет использовать JWT клиента StarVault в качестве собственного auth-токена при взаимодействии с Kubernetes TokenReview API. Если StarVault работает в Kubernetes, также нужно установить disable_local_ca_jwt=true.

Это означает, что StarVault не хранит никаких JWT и позволяет использовать недолговечные токены повсеместно, но добавляет некоторые операционные накладные расходы на поддержание привязки ролей кластера к набору учетных записей служб, которые хотите аутентифицировать в StarVault. Каждому клиенту StarVault потребуется кластерная роль (ClusterRole)system:auth-delegator:

kubectl create clusterrolebinding starvault-client-auth-delegator \
  --clusterrole=system:auth-delegator \
  --group=group1 \
  --serviceaccount=default:svcaccount1 \
  ...

3.1.3. Продолжайте использовать долгоживущие токены

Можете создать долгоживущий секрет, используя инструкции здесь, и использовать его в качестве token_reviewer_jwt. В этом примере учетной записи службы хранилища потребуется роль (ClusterRole) system:auth-delegator:

kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: starvault-k8s-auth-secret
  annotations:
    kubernetes.io/service-account.name: starvault
type: kubernetes.io/service-account-token
EOF

Использование этого способа позволяет сохранить прежние рабочие процессы, но не получить преимущества от повышения уровня безопасности недолговечных токенов.

3.1.4. Используйте JWT-авторизацию

Аутентификация Kubernetes специализируется на использовании API TokenReview от Kubernetes. Однако JWT-токены, которые генерирует Kubernetes, также можно проверять, используя Kubernetes в качестве OIDC-провайдера. В документации по методу JWT-аутентификации есть инструкции по настройке JWT-аутентификацию с Kubernetes в качестве OIDC-провайдера.

Это решение позволяет использовать недолговечные токены для всех клиентов и устраняет необходимость в ревьювере JWT. Однако клиентские токены не могут быть отозваны до истечения их TTL, поэтому рекомендуется держать TTL коротким с учетом этого ограничения.

3.2. Обнаружение issuer учетной записи обслуживания

Следующий раздел применим только в том случае, если установлено disable_iss_validation=false, но disable_iss_validation=true - это рекомендуемое значение для всех версий StarVault.

Для кластеров Kubernetes 1.21+ может потребоваться установить эмитента (issuer) учетной записи сервиса в то же значение, что и флаг kube-apiserver --service-account-issuer. Это связано с тем, что JWT-файлы учетных записей сервисов для этих кластеров могут иметь эмитента, специфичного для самого кластера, вместо старого значения по умолчанию kubernetes/serviceaccount. Если нет возможности проверить это значение напрямую, то можете выполнить следующую процедуру и найти нужное значение в поле "iss":

echo '{"apiVersion": "authentication.k8s.io/v1", "kind": "TokenRequest"}' \
  | kubectl create -f- --raw /api/v1/namespaces/default/serviceaccounts/default/token \
  | jq -r '.status.token' \
  | cut -d . -f2 \
  | base64 -d

В большинстве кластеров эта информация также доступна в конечной точке .well-known/openid-configuration:

kubectl get --raw /.well-known/openid-configuration | jq -r .issuer

Это значение затем используется при настройке аутентификации Kubernetes, например:

starvault write auth/kubernetes/config \
  kubernetes_host="https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT" \
  issuer="\"test-aks-cluster-dns-d6cbb78e.hcp.uksouth.azmk8s.io\""

4. Конфигурация kubernetes

Этот метод авторизации обращается к API Kubernetes TokenReview для проверки того, что предоставленный JWT все еще действителен. Kubernetes должен быть запущен с параметром --service-account-lookup. Начиная с Kubernetes 1.7 по умолчанию установлено значение true. В противном случае удаленные токены в Kubernetes не будут должным образом отозваны и смогут аутентифицироваться в этом методе авторизации.

Учетные записи служб, используемые в этом методе аутентификации, должны иметь доступ к API TokenReview. Если Kubernetes настроена на использование ролей RBAC, учетной записи службы должны быть предоставлены разрешения на доступ к этому API. Следующий пример ClusterRoleBinding может быть использован для предоставления этих разрешений:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: role-tokenreview-binding
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
  - kind: ServiceAccount
    name: starvault-auth
    namespace: default

5. API

Плагин Kubernetes Auth имеет полноценный HTTP API. Более подробную информацию можно найти в документации по API.

6. Пример кода

Следующий пример демонстрирует метод Kubernetes для аутентификации в StarVault.

package main

import (
    "fmt"
    "os"

    vault "github.com/hashicorp/vault/api"
    auth "github.com/hashicorp/vault/api/auth/kubernetes"
)

// Fetches a key-value secret (kv-v2) after authenticating to Vault with a Kubernetes service account.
// For a more in-depth setup explanation, please see the relevant readme in the hashicorp/vault-examples repo.
func getSecretWithKubernetesAuth() (string, error) {
    // If set, the VAULT_ADDR environment variable will be the address that
    // your pod uses to communicate with Vault.
    config := vault.DefaultConfig() // modify for more granular configuration

    client, err := vault.NewClient(config)
    if err != nil {
        return "", fmt.Errorf("unable to initialize Vault client: %w", err)
    }

    // The service-account token will be read from the path where the token's
    // Kubernetes Secret is mounted. By default, Kubernetes will mount it to
    // /var/run/secrets/kubernetes.io/serviceaccount/token, but an administrator
    // may have configured it to be mounted elsewhere.
    // In that case, we'll use the option WithServiceAccountTokenPath to look
    // for the token there.
    k8sAuth, err := auth.NewKubernetesAuth(
        "dev-role-k8s",
        auth.WithServiceAccountTokenPath("path/to/service-account-token"),
    )
    if err != nil {
        return "", fmt.Errorf("unable to initialize Kubernetes auth method: %w", err)
    }

    authInfo, err := client.Auth().Login(context.TODO(), k8sAuth)
    if err != nil {
        return "", fmt.Errorf("unable to log in with Kubernetes auth: %w", err)
    }
    if authInfo == nil {
        return "", fmt.Errorf("no auth info was returned after login")
    }

    // get secret from Vault, from the default mount path for KV v2 in dev mode, "secret"
    secret, err := client.KVv2("secret").Get(context.Background(), "creds")
    if err != nil {
        return "", fmt.Errorf("unable to read secret: %w", err)
    }

    // data map can contain more than one key-value pair,
    // in this case we're just grabbing one of them
    value, ok := secret.Data["password"].(string)
    if !ok {
        return "", fmt.Errorf("value type assertion failed: %T %#v", secret.Data["password"], secret.Data["password"])
    }

    return value, nil
}
using System;
using System.IO;
using VaultSharp;
using VaultSharp.V1.AuthMethods;
using VaultSharp.V1.AuthMethods.Kubernetes;
using VaultSharp.V1.Commons;

namespace Examples
{
    public class KubernetesAuthExample
    {
        const string DefaultTokenPath = "path/to/service-account-token";

        // Fetches a key-value secret (kv-v2) after authenticating to Vault with a Kubernetes service account.
        // For a more in-depth setup explanation, please see the relevant readme in the hashicorp/vault-examples repo.
        public string GetSecretWithK8s()
        {
            var vaultAddr = Environment.GetEnvironmentVariable("VAULT_ADDR");
            if(String.IsNullOrEmpty(vaultAddr))
            {
                throw new System.ArgumentNullException("Vault Address");
            }

            var roleName = Environment.GetEnvironmentVariable("VAULT_ROLE");
            if(String.IsNullOrEmpty(roleName))
            {
                throw new System.ArgumentNullException("Vault Role Name");
            }

            // Get the path to service account token or fall back on default path
            string pathToToken = String.IsNullOrEmpty(Environment.GetEnvironmentVariable("SA_TOKEN_PATH")) ? DefaultTokenPath : Environment.GetEnvironmentVariable("SA_TOKEN_PATH");
            string jwt = File.ReadAllText(pathToToken);

            IAuthMethodInfo authMethod = new KubernetesAuthMethodInfo(roleName, jwt);
            var vaultClientSettings = new VaultClientSettings(vaultAddr, authMethod);

            IVaultClient vaultClient = new VaultClient(vaultClientSettings);

            // We can retrieve the secret after creating our VaultClient object
            Secret<SecretData> kv2Secret = null;
            kv2Secret = vaultClient.V1.Secrets.KeyValue.V2.ReadSecretAsync(path: "/creds").Result;

            var password = kv2Secret.Data.Data["password"];

            return password.ToString();
        }
    }
}