Vault — это система управления секретами от HashiCorp, которая позволяет безопасно хранить и получать доступ к токенам, паролям, сертификатам и другим чувствительным данным. В Kubernetes Vault используется как внешнее хранилище секретов и может обеспечивать централизованное управление доступом к ним.
Установка
Установить Vault в кластер можно через панель управления. Для этого:
Перейдите во вкладку «Дополнения» на странице управления кластером. Найдите карточку «Vault» и нажмите «Установить».
В появившемся окне вы можете изменить параметры установки:
-
Переключитесь в режим продвинутой установки;
-
Отредактируйте конфигурацию вручную или загрузите свой файл
values.yaml
; -
Нажмите кнопку «Установить».
Конфигурация по умолчанию подразумевает что дополнение будет использоваться в dev-режиме. Это упрощенный режим, в котором Vault автоматически инициализируется, не требует настройки хранилища и использует предустановленный root token
. Такой режим подходит только для тестирования и разработки — он небезопасен для продакшена.
Проверка установки
После завершения установки дополнения убедитесь, что компоненты Vault успешно запустились. Для этого выполните команду:
kubectl get pods -n vault
Вы должны увидеть список подов, среди которых:
-
vault-0
— основной под Vault; -
vault-agent-injector-xxx
— сервис, отвечающий за автоматическую подстановку секретов в поды; -
дополнительные поды (например,
vault-1
,vault-2
), если установлен в HA-режиме.
Если в конфигурации была включена опция ui = true
(по умолчанию включена), вы можете получить доступ к веб-интерфейсу Vault:
Пробросьте порт с помощью следующей команды:
kubectl port-forward -n vault svc/vault 8200:8200
Откройте браузер и перейдите по адресу:
http://localhost:8200
В dev-режиме Vault уже инициализирован, и вы можете авторизоваться с помощью токена, указанного в конфигурации. По умолчанию значение такое:
devRootToken: "root"
Режим HA
Режим HA (High Availability) позволяет развернуть несколько экземпляров Vault с распределенным хранением данных. Это обеспечивает:
- отказоустойчивость — при выходе из строя одного экземпляра кластер продолжит работать;
- централизованное хранилище;
- возможность масштабирования.
Один из подов становится лидером, остальные работают в режиме standby
. Запросы на чтение/запись обрабатывает только лидер, но в случае его недоступности управление автоматически перейдет к одному из standby
-подов.
Для работы в этом режиме рекомендуем установить и использовать дополнения CSI-S3 или CSI-driver.
Чтобы включить режим HA, в конфигурации дополнения укажите параметры:
server.dev.enabled: false
— отключаем dev-режим;server.standalone.enabled: false
— отключаем standalone-режим;server.ha.enabled: true
— включаем HA-режим;server.ha.replicas: 3
— указываем количество реплик;server.ha.raft.enabled: true
— используем встроенное хранилище Raft;server.dataStorage.storageClass: csi-s3
— подключаем S3-хранилище через CSI.
Пример конфигурации (нажмите, чтобы развернуть)
global:
enabled: true
namespace: ""
imagePullSecrets: []
tlsDisable: true
externalVaultAddr: ""
openshift: false
psp:
enable: false
annotations: |
seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default,runtime/default
apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default
seccomp.security.alpha.kubernetes.io/defaultProfileName: runtime/default
apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default
serverTelemetry:
prometheusOperator: false
injector:
enabled: true
replicas: 1
port: 8080
leaderElector:
enabled: true
metrics:
enabled: false
externalVaultAddr: ""
image:
repository: "hashicorp/vault-k8s"
tag: "1.7.0"
pullPolicy: IfNotPresent
agentImage:
repository: "hashicorp/vault"
tag: "1.20.4"
agentDefaults:
cpuLimit: "500m"
cpuRequest: "250m"
memLimit: "128Mi"
memRequest: "64Mi"
template: "map"
templateConfig:
exitOnRetryFailure: true
staticSecretRenderInterval: ""
livenessProbe:
failureThreshold: 2
initialDelaySeconds: 5
periodSeconds: 2
successThreshold: 1
timeoutSeconds: 5
readinessProbe:
failureThreshold: 2
initialDelaySeconds: 5
periodSeconds: 2
successThreshold: 1
timeoutSeconds: 5
startupProbe:
failureThreshold: 12
initialDelaySeconds: 5
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 5
authPath: "auth/kubernetes"
logLevel: "info"
logFormat: "standard"
revokeOnShutdown: false
webhook:
failurePolicy: Ignore
matchPolicy: Exact
timeoutSeconds: 30
namespaceSelector: {}
objectSelector: |
matchExpressions:
- key: app.kubernetes.io/name
operator: NotIn
values:
- {{ template "vault.name" . }}-agent-injector
annotations: {}
failurePolicy: Ignore
namespaceSelector: {}
objectSelector: {}
webhookAnnotations: {}
certs:
secretName: null
caBundle: ""
certName: tls.crt
keyName: tls.key
securityContext:
pod: {}
container: {}
resources: {}
extraEnvironmentVars: {}
affinity: |
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector
app.kubernetes.io/instance: "{{ .Release.Name }}"
component: webhook
topologyKey: kubernetes.io/hostname
topologySpreadConstraints: []
tolerations: []
nodeSelector: {}
priorityClassName: ""
annotations: {}
extraLabels: {}
hostNetwork: false
service:
annotations: {}
serviceAccount:
annotations: {}
podDisruptionBudget: {}
strategy: {}
server:
enabled: true
enterpriseLicense:
secretName: ""
secretKey: "license"
image:
repository: "hashicorp/vault"
tag: "1.20.4"
pullPolicy: IfNotPresent
updateStrategyType: "OnDelete"
logLevel: "info"
logFormat: "standard"
resources: {}
ingress:
enabled: false
labels: {}
annotations: {}
ingressClassName: ""
pathType: Prefix
activeService: true
hosts:
- host: chart-example.local
paths: []
extraPaths: []
tls: []
hostAliases: []
route:
enabled: false
activeService: true
labels: {}
annotations: {}
host: chart-example.local
tls:
termination: passthrough
authDelegator:
enabled: true
extraInitContainers: null
extraContainers: null
shareProcessNamespace: false
extraArgs: ""
extraPorts: null
readinessProbe:
enabled: true
port: 8200
failureThreshold: 2
initialDelaySeconds: 5
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 3
livenessProbe:
enabled: false
execCommand: []
path: "/v1/sys/health?standbyok=true"
port: 8200
failureThreshold: 2
initialDelaySeconds: 60
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 3
terminationGracePeriodSeconds: 10
preStopSleepSeconds: 5
preStop: []
postStart: []
extraEnvironmentVars: {}
extraSecretEnvironmentVars: []
extraVolumes: []
volumes: null
volumeMounts: null
affinity: |
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app.kubernetes.io/name: {{ template "vault.name" . }}
app.kubernetes.io/instance: "{{ .Release.Name }}"
component: server
topologyKey: kubernetes.io/hostname
topologySpreadConstraints: []
tolerations: []
nodeSelector: {}
networkPolicy:
enabled: false
egress: []
ingress:
- from:
- namespaceSelector: {}
ports:
- port: 8200
protocol: TCP
- port: 8201
protocol: TCP
priorityClassName: ""
extraLabels: {}
annotations: {}
includeConfigAnnotation: false
service:
enabled: true
active:
enabled: true
annotations: {}
standby:
enabled: true
annotations: {}
instanceSelector:
enabled: true
ipFamilyPolicy: ""
ipFamilies: []
publishNotReadyAddresses: true
externalTrafficPolicy: Cluster
port: 8200
targetPort: 8200
annotations: {}
dataStorage:
enabled: true
size: 1Gi
mountPath: "/vault/data"
storageClass: "csi-s3"
accessMode: ReadWriteOnce
annotations: {}
labels: {}
persistentVolumeClaimRetentionPolicy: {}
auditStorage:
enabled: false
size: 1Gi
mountPath: "/vault/audit"
storageClass: "csi-s3"
accessMode: ReadWriteOnce
annotations: {}
labels: {}
dev:
enabled: false
devRootToken: "root"
standalone:
enabled: false
config: |-
ui = true
listener "tcp" {
tls_disable = 1
address = "[::]:8200"
cluster_address = "[::]:8201"
}
storage "file" {
path = "/vault/data"
}
ha:
enabled: true
replicas: 3
apiAddr: null
clusterAddr: null
raft:
enabled: true
setNodeId: false
config: |
ui = true
listener "tcp" {
tls_disable = 1
address = "[::]:8200"
cluster_address = "[::]:8201"
}
storage "raft" {
path = "/vault/data"
}
service_registration "kubernetes" {}
disruptionBudget:
enabled: true
maxUnavailable: null
serviceAccount:
create: true
name: ""
createSecret: false
annotations: {}
extraLabels: {}
serviceDiscovery:
enabled: true
statefulSet:
annotations: {}
securityContext:
pod: {}
container: {}
hostNetwork: false
ui:
enabled: true
publishNotReadyAddresses: true
activeVaultPodOnly: false
serviceType: "ClusterIP"
serviceNodePort: null
externalPort: 8200
targetPort: 8200
serviceIPFamilyPolicy: ""
serviceIPFamilies: []
externalTrafficPolicy: Cluster
annotations: {}
csi:
enabled: true
image:
repository: "hashicorp/vault-csi-provider"
tag: "1.5.1"
pullPolicy: IfNotPresent
volumes: null
volumeMounts: null
resources: {}
hmacSecretName: ""
hostNetwork: false
daemonSet:
updateStrategy:
type: RollingUpdate
maxUnavailable: ""
annotations: {}
providersDir: "/var/run/secrets-store-csi-providers"
kubeletRootDir: "/var/lib/kubelet"
extraLabels: {}
securityContext:
pod: {}
container: {}
pod:
annotations: {}
tolerations: []
nodeSelector: {}
affinity: {}
extraLabels: {}
agent:
enabled: true
extraArgs: []
image:
repository: "hashicorp/vault"
tag: "1.20.4"
pullPolicy: IfNotPresent
logFormat: standard
logLevel: info
resources: {}
securityContext:
container:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 100
runAsGroup: 1000
priorityClassName: ""
serviceAccount:
annotations: {}
extraLabels: {}
readinessProbe:
failureThreshold: 2
initialDelaySeconds: 5
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 3
livenessProbe:
failureThreshold: 2
initialDelaySeconds: 5
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 3
logLevel: "info"
debug: false
extraArgs: []
serverTelemetry:
serviceMonitor:
enabled: false
selectors: {}
interval: 30s
scrapeTimeout: 10s
tlsConfig: {}
authorization: {}
metricRelabelings: []
prometheusRules:
enabled: false
selectors: {}
rules: []
После установки кластера с включенным HA-режимом Vault не инициализируется автоматически. Это нужно сделать вручную с помощью CLI.
Для инициализации Vault подключитесь к основному поду (vault-0
) :
kubectl exec -it vault-0 -n vault -- /bin/sh
И выполните команды:
export VAULT_ADDR=http://127.0.0.1:8200
export VAULT_CLIENT_TIMEOUT=300s
vault operator init
В результате вы получите несколько Unseal Keys
и Initial Root Token
. Сохраните эти данные в надежном месте — без них восстановить доступ к Vault будет невозможно.
Пример вывода:
Unseal Key 1: 4ErPXwe87rjULP6yz7h3XZ8Dr/nhTyMrVLiIsQ8s5ksX
Unseal Key 2: IVk3hipR5D/yR5ngi1LJaaxRwarEWjR/hjC8DFwXuNYb
Unseal Key 3: qBCx+7B+wiehep0yArs7nVT73SyMYXh+AH3jCXTCs80H
Unseal Key 4: CQm+0tOTS9wZQWYJJU8Roo2tMCGS+dZt7eXMDLjU5gX+
Unseal Key 5: KTvyD+vhEXPNQgcQJQe69Gu/sjkhhl/ScGZNnmmN64xC
Initial Root Token: hvs.uKO8ZtmUgARVtrLhzBlQV4tA
По умолчанию создается 5 ключей, из которых требуется минимум 3 для активации хранилища.
После инициализации Vault находится в sealed
состоянии. Чтобы запустить кластер, его нужно активировать. Для этого:
-
Подключитесь к каждому поду по очереди (
vault-0
,vault-1
,vault-2
).
-
Выполните команду, указав три любых из полученных ключей:
vault operator unseal <UNSEAL_KEY_1>
vault operator unseal <UNSEAL_KEY_2>
vault operator unseal <UNSEAL_KEY_3>
-
Проверьте статус:
vault status
Если все прошло успешно, Sealed
будет иметь значение false
.