Сканирование контейнерных образов на уязвимости
Платформа Deckhouse Kubernetes Platform (DKP) построена с учётом стандартов CIS Kubernetes Benchmark, что гарантирует высокий уровень защищённости как её внутренних модулей, так и инфраструктуры в целом.
Для подтверждения этого уровня в каждом кластере в фоновом режиме функционируют регулярные проверки на соответствие стандартам CIS. Итоговые данные визуализируются в Grafana на специальной панели Security / CIS Kubernetes Benchmark в виде детализированных отчётов. Кроме того, в DKP интегрирован инструмент для автоматического анализа контейнерных образов на наличие уязвимостей, функционирующий на базе Trivy. Для оперативной работы с данными сканирования предусмотрены команды, позволяющие выводить и фильтровать отчёты по уязвимостям и результатам CIS-тестов непосредственно в кластере.
Доступ к результатам сканирования
Доступ к результатам сканирования, в том числе возможность просматривать ресурсы с результатами предоставляется пользователям, обладающим следующими ролями доступа:
- d8:manage:networking:viewer или выше;
- d8:manage:permission:module:operator-trivy:view.
Просмотр отчёта сканирования своего приложения
Для просмотра результатов сканирования вашего приложения воспользуйтесь Grafana-дашбордом Security / Trivy Image Vulnerability Overview. Вы можете отфильтровать результаты по нужному пространству имён и ресурсу.
Просмотр результатов CIS compliance-проверки
- Чтобы вывести все ресурсы, которые не прошли проверку:
d8 k get clustercompliancereports.aquasecurity.github.io cis -ojson |
jq '.status.detailReport.results | map(sel ect(.checks | map(.success) | all | not))'
- Чтобы выполнить поиск по идентификатору конкретной проверки:
check_id="5.7.3"
d8 k get clustercompliancereports.aquasecurity.github.io cis -ojson |
jq --arg check_id "$check_id" '.status.detailReport.results | map(sel ect(.id == $check_id))'
- Чтобы выполнить поиск по описанию проверки:
check_desc="Apply Security Context to Your Pods and Containers"
d8 k get clustercompliancereports.aquasecurity.github.io cis -ojson |
jq --arg check_desc "$check_desc" '.status.detailReport.results | map(select(.description == $check_desc))'
Просмотр результатов сканирования
Дашборды в Grafana:
- Security/Trivy Image Vulnerability Overview — сводный обзор уязвимостей в образах контейнеров, запущенных в кластере.
- Security/CIS Kubernetes Benchmark — результаты проверки соответствия кластера требованиям CIS Kubernetes Benchmark.
В Kubernetes-кластере информация о состоянии безопасности представлена двумя категориями отчетов. К первой категории относятся отчеты, характеризующие безопасность самого кластера: ClusterComplianceReport и RbacAssessmentReport. Ко второй категории — отчеты, описывающие безопасность рабочей нагрузки (нагрузки):
- VulnerabilityReport — содержит данные об уязвимостях в контейнерных образах;
- SbomReport — предоставляет детализированный состав программного обеспечения (SBOM);
- ConfigAuditReport — включает результаты аудита конфигурации объектов Kubernetes на предмет ошибок;
- ExposedSecretReport — сигнализирует о потенциальной утечке секретов внутри контейнеров.
В основе сбора этих данных лежит набор пользовательских ресурсов (CRD) от проекта Trivy Operator (разработка Aqua Security), которые интегрированы в DKP. Эти ресурсы стандартизируют представление результатов сканирования уязвимостей, анализа конфигурации и проверок безопасности. Далее приведено описание ключевых CRD, создаваемых operator-trivy, с практическими примерами и ссылками на официальную документацию.
Описание ресурсов с результатами сканирования
Безопасность на уровне ресурсов
VulnerabilityReport
Ресурс VulnerabilityReport аккумулирует данные сканирования контейнерных образов, используемых в рабочих нагрузках Kubernetes. В отчете приводится полный перечень известных уязвимостей, затрагивающих как системные пакеты, так и прикладное ПО, с сортировкой по критичности (Critical, High, Medium и др.).
При сканировании мультиконтейнерных подов operator-trivy генерирует отдельный экземпляр VulnerabilityReport для каждого контейнера. Эти ресурсы создаются в том же неймспейсе, что и исходная нагрузка, а их привязка к родительскому объекту Kubernetes реализуется через поле ownerReference. Шаблон именования отчета имеет следующий вид: <тип рабочей нагрузки>-<имя рабочей нагрузки>-<имя контейнера>.
Пример:
apiVersion: aquasecurity.github.io/v1alpha1
kind: VulnerabilityReport
metadata:
name: replicaset-nginx-6d4cf56db6-nginx
namespace: default
labels:
trivy-operator.container.name: nginx
trivy-operator.resource.kind: ReplicaSet
trivy-operator.resource.name: nginx-6d4cf56db6
trivy-operator.resource.namespace: default
resource-spec-hash: 7cb64cb677
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: nginx-6d4cf56db6
uid: aa345200-cf24-443a-8f11-ddb438ff8659
controller: true
blockOwnerDeletion: false
report:
artifact:
repository: library/nginx
tag: '1.16'
os:
family: debian
name: '10.3'
registry:
server: index.docker.io
scanner:
name: Trivy
vendor: Aqua Security
version: 0.35.0
summary:
criticalCount: 2
highCount: 0
mediumCount: 0
lowCount: 0
unknownCount: 0
vulnerabilities:
- vulnerabilityID: CVE-2019-20367
resource: libbsd0
installedVersion: 0.9.1-2
fixedVersion: 0.9.1-2+deb10u1
severity: CRITICAL
score: 9.1
target: library/nginx:1.21.6
primaryLink: https://avd.aquasec.com/nvd/cve-2019-20367
- vulnerabilityID: CVE-2018-25009
resource: libwebp6
installedVersion: 0.6.1-2
fixedVersion: ''
severity: CRITICAL
score: 9.1
target: library/nginx:1.16
title: 'libwebp: out-of-bounds read in WebPMuxCreateInternal'
primaryLink: https://avd.aquasec.com/nvd/cve-2018-25009
ConfigAuditReport
Ресурс ConfigAuditReport предназначен для хранения результатов аудита конфигурации объектов Kubernetes, выполняемого с помощью инструментов статического анализа, таких как Trivy. В отчете приводится перечень выявленных замечаний, сгруппированных по функциональным категориям (например, "Security") и уровням критичности (Critical, High и т.д.).
Типовые проверки охватывают следующие аспекты:
- идентификатор запуска контейнера (проверка на запуск от root-пользователя);
- наличие и корректность ресурсных лимитов;
- параметры сетевой изоляции (использование hostNetwork, hostPID);
- настройки безопасности, предотвращающие эскалацию привилегий (например, allowPrivilegeEscalation).
ConfigAuditReport генерируется для всех типов ресурсов в пределах namespace: как для workloads (Pod, Deployment, StatefulSet), так и для вспомогательных объектов (Service, ConfigMap, Role, RoleBinding). Каждый отчет привязан к исходному объекту через поле ownerReference и хранится в том же пространстве имен. Имя ресурса формируется по шаблону <тип_объекта>-<имя_объекта>.
Пример:
apiVersion: aquasecurity.github.io/v1alpha1
kind: VulnerabilityReport
metadata:
name: replicaset-nginx-6d4cf56db6-nginx
namespace: default
labels:
trivy-operator.container.name: nginx
trivy-operator.resource.kind: ReplicaSet
trivy-operator.resource.name: nginx-6d4cf56db6
trivy-operator.resource.namespace: default
resource-spec-hash: 7cb64cb677
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: nginx-6d4cf56db6
uid: aa345200-cf24-443a-8f11-ddb438ff8659
controller: true
blockOwnerDeletion: false
report:
artifact:
repository: library/nginx
tag: '1.16'
os:
family: debian
name: '10.3'
registry:
server: index.docker.io
scanner:
name: Trivy
vendor: Aqua Security
version: 0.35.0
summary:
criticalCount: 2
highCount: 0
mediumCount: 0
lowCount: 0
unknownCount: 0
vulnerabilities:
- vulnerabilityID: CVE-2019-20367
resource: libbsd0
installedVersion: 0.9.1-2
fixedVersion: 0.9.1-2+deb10u1
severity: CRITICAL
score: 9.1
target: library/nginx:1.21.6
primaryLink: https://avd.aquasec.com/nvd/cve-2019-20367
- vulnerabilityID: CVE-2018-25009
resource: libwebp6
installedVersion: 0.6.1-2
fixedVersion: ''
severity: CRITICAL
score: 9.1
target: library/nginx:1.16
title: 'libwebp: out-of-bounds read in WebPMuxCreateInternal'
primaryLink: https://avd.aquasec.com/nvd/cve-2018-25009
ExposedSecretReport
Ресурс ExposedSecretReport предназначен для хранения результатов детектирования конфиденциальной информации внутри контейнерных образов, используемых в рабочих нагрузках Kubernetes. Отчет содержит перечень строк, идентифицированных как потенциальные секреты (токены, ключи доступа, пароли), с указанием точного местоположения в файловой системе образа. Каждая находка сопровождается метаданными: категория уязвимости, сработавшее правило проверки, уровень критичности и путь к файлу.
При сканировании мультиконтейнерных подов operator-trivy генерирует отдельный экземпляр ExposedSecretReport для каждого контейнера. Эти ресурсы создаются в том же пространстве имен, что и исходная нагрузка, а их привязка к родительскому объекту Kubernetes реализуется через поле ownerReference. Имя отчета формируется по шаблону <тип рабочей нагрузки>-<имя рабочей нагрузки>-<имя контейнера>.
Пример:
apiVersion: aquasecurity.github.io/v1alpha1
kind: ExposedSecretReport
metadata:
name: replicaset-app-67b77f5965-app
namespace: default
labels:
trivy-operator.container.name: app
trivy-operator.resource.kind: ReplicaSet
trivy-operator.resource.name: app-67b77f5965
trivy-operator.resource.namespace: default
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: app-67b77f5965
report:
artifact:
repository: myimagewithsecret
tag: v0.22.0
registry:
server: index.docker.io
scanner:
name: Trivy
vendor: Aqua Security
version: 0.35.0
secrets:
- category: Stripe
ruleID: stripe-access-token
severity: HIGH
target: "/app/config/secret.yaml"
match: "publishable_key: *****"
title: Stripe
- category: Stripe
ruleID: stripe-access-token
severity: HIGH
target: "/app/config/secret.yaml"
match: "secret_key: *****"
title: Stripe
summary:
criticalCount: 0
highCount: 2
mediumCount: 0
lowCount: 0
updateTimestamp: "2022-06-29T14:29:37Z"
SbomReport
Ресурс SbomReport представляет собой инвентаризационную ведомость программного обеспечения (Software Bill of Materials — SBOM) для контейнерного образа, используемого в рабочих нагрузках Kubernetes. Отчет содержит исчерпывающий перечень всех программных компонентов, обнаруженных внутри образа, включая пакеты операционной системы и зависимости приложений. Данная информация необходима для анализа содержимого образа, проведения аудита безопасности и подтверждения соответствия требованиям вендоров и регуляторов.
При сканировании мультиконтейнерных подов operator-trivy генерирует отдельный экземпляр SbomReport для каждого контейнера. Эти ресурсы создаются в том же пространстве имен, что и исходная нагрузка, а их привязка к родительскому объекту Kubernetes реализуется через поле ownerReference. Имя отчета формируется по шаблону <тип рабочей нагрузки>-<имя рабочей нагрузки>-<имя контейнера>.
Пример:
apiVersion: aquasecurity.github.io/v1alpha1
kind: SbomReport
metadata:
creationTimestamp: "2023-07-10T09:37:21Z"
generation: 1
labels:
resource-spec-hash: 796669cd5d
trivy-operator.container.name: kube-apiserver
trivy-operator.resource.kind: Pod
trivy-operator.resource.name: kube-apiserver-kind-control-plane
trivy-operator.resource.namespace: kube-system
name: pod-kube-apiserver-kind-control-plane-kube-apiserver
namespace: kube-system
ownerReferences:
- apiVersion: v1
blockOwnerDeletion: false
controller: true
kind: Pod
name: kube-apiserver-kind-control-plane
uid: 732b4aa7-91f8-40a3-8b21-9627a98a910b
resourceVersion: "6148"
uid: 2a5000fe-b97e-46d0-9de7-62fb5fbc6555
report:
artifact:
repository: kube-apiserver
tag: v1.21.1
components:
bomFormat: CycloneDX
components:
- bom-ref: 9464f5f9-750d-4ea0-8705-c8d067b25b29
name: debian
properties:
- name: aquasecurity:trivy:Class
value: os-pkgs
- name: aquasecurity:trivy:Type
value: debian
supplier: {}
type: operating-system
version: "10.9"
- bom-ref: pkg:deb/debian/base-files@10.3+deb10u9?arch=amd64&distro=debian-10.9
licenses:
- expression: GPL-3.0
license: {}
name: base-files
properties:
- name: aquasecurity:trivy:LayerDiffID
value: sha256:417cb9b79adeec55f58b890dc9831e252e3523d8de5fd28b4ee2abb151b7dc8b
- name: aquasecurity:trivy:LayerDigest
value: sha256:5dea5ec2316d4a067b946b15c3c4f140b4f2ad607e73e9bc41b673ee5ebb99a3
- name: aquasecurity:trivy:PkgID
value: base-files@10.3+deb10u9
- name: aquasecurity:trivy:PkgType
value: debian
- name: aquasecurity:trivy:SrcName
value: base-files
- name: aquasecurity:trivy:SrcVersion
value: 10.3+deb10u9
purl: pkg:deb/debian/base-files@10.3+deb10u9?arch=amd64&distro=debian-10.9
supplier:
name: Santiago Vila
type: library
version: 10.3+deb10u9
- bom-ref: pkg:deb/debian/netbase@5.6?arch=all&distro=debian-10.9
licenses:
- expression: GPL-2.0
license: {}
name: netbase
properties:
- name: aquasecurity:trivy:LayerDiffID
value: sha256:417cb9b79adeec55f58b890dc9831e252e3523d8de5fd28b4ee2abb151b7dc8b
- name: aquasecurity:trivy:LayerDigest
value: sha256:5dea5ec2316d4a067b946b15c3c4f140b4f2ad607e73e9bc41b673ee5ebb99a3
- name: aquasecurity:trivy:PkgID
value: netbase@5.6
- name: aquasecurity:trivy:PkgType
value: debian
- name: aquasecurity:trivy:SrcName
value: netbase
- name: aquasecurity:trivy:SrcVersion
value: "5.6"
purl: pkg:deb/debian/netbase@5.6?arch=all&distro=debian-10.9
supplier:
name: Marco d'Itri
type: library
version: "5.6"
- bom-ref: pkg:deb/debian/tzdata@2021a-0+deb10u1?arch=all&distro=debian-10.9
name: tzdata
properties:
- name: aquasecurity:trivy:LayerDiffID
value: sha256:417cb9b79adeec55f58b890dc9831e252e3523d8de5fd28b4ee2abb151b7dc8b
- name: aquasecurity:trivy:LayerDigest
value: sha256:5dea5ec2316d4a067b946b15c3c4f140b4f2ad607e73e9bc41b673ee5ebb99a3
- name: aquasecurity:trivy:PkgID
value: tzdata@2021a-0+deb10u1
- name: aquasecurity:trivy:PkgType
value: debian
- name: aquasecurity:trivy:SrcName
value: tzdata
- name: aquasecurity:trivy:SrcRelease
value: 0+deb10u1
- name: aquasecurity:trivy:SrcVersion
value: 2021a
purl: pkg:deb/debian/tzdata@2021a-0+deb10u1?arch=all&distro=debian-10.9
supplier:
name: GNU Libc Maintainers
type: library
version: 2021a-0+deb10u1
dependencies:
- dependsOn:
- pkg:deb/debian/base-files@10.3+deb10u9?arch=amd64&distro=debian-10.9
- pkg:deb/debian/netbase@5.6?arch=all&distro=debian-10.9
- pkg:deb/debian/tzdata@2021a-0+deb10u1?arch=all&distro=debian-10.9
ref: 9464f5f9-750d-4ea0-8705-c8d067b25b29
- dependsOn: []
ref: pkg:deb/debian/base-files@10.3+deb10u9?arch=amd64&distro=debian-10.9
- dependsOn: []
ref: pkg:deb/debian/netbase@5.6?arch=all&distro=debian-10.9
- dependsOn: []
ref: pkg:deb/debian/tzdata@2021a-0+deb10u1?arch=all&distro=debian-10.9
- dependsOn:
- 9464f5f9-750d-4ea0-8705-c8d067b25b29
ref: pkg:oci/kube-apiserver@sha256:53a13cd1588391888c5a8ac4cef13d3ee6d229cd904038936731af7131d193a9?repository_url=k8s.gcr.io%2Fkube-apiserver&arch=amd64
metadata:
component:
bom-ref: pkg:oci/kube-apiserver@sha256:53a13cd1588391888c5a8ac4cef13d3ee6d229cd904038936731af7131d193a9?repository_url=k8s.gcr.io%2Fkube-apiserver&arch=amd64
name: k8s.gcr.io/kube-apiserver:v1.21.1
properties:
- name: aquasecurity:trivy:DiffID
value: sha256:417cb9b79adeec55f58b890dc9831e252e3523d8de5fd28b4ee2abb151b7dc8b,sha256:b50131762317bbe47def2d426d5c78a353a08b966d36bed4a04aee99dde4e12b,sha256:1e6ed7621dee7e03dd779486ed469a65af6fb13071d13bd3a89c079683e3b1f0
- name: aquasecurity:trivy:ImageID
value: sha256:771ffcf9ca634e37cbd3202fd86bd7e2df48ecba4067d1992541bfa00e88a9bb
- name: aquasecurity:trivy:RepoDigest
value: k8s.gcr.io/kube-apiserver@sha256:53a13cd1588391888c5a8ac4cef13d3ee6d229cd904038936731af7131d193a9
- name: aquasecurity:trivy:RepoTag
value: k8s.gcr.io/kube-apiserver:v1.21.1
- name: aquasecurity:trivy:SchemaVersion
value: "2"
purl: pkg:oci/kube-apiserver@sha256:53a13cd1588391888c5a8ac4cef13d3ee6d229cd904038936731af7131d193a9?repository_url=k8s.gcr.io%2Fkube-apiserver&arch=amd64
supplier: {}
type: container
timestamp: "2023-07-10T09:37:21+00:00"
tools:
- name: trivy
vendor: aquasecurity
serialNumber: urn:uuid:50dbce86-28c5-4caf-9d08-a4aadf23233e
specVersion: 1.4
version: 1
registry:
server: k8s.gcr.io
scanner:
name: Trivy
vendor: Aqua Security
version: 0.52.2
summary:
componentsCount: 5
dependenciesCount: 5
updateTimestamp: "2023-07-10T09:37:21Z
Безопасность на уровне кластера
RbacAssessmentReport
Ресурс RbacAssessmentReport аккумулирует результаты аудита конфигурации ролевой модели доступа (RBAC) в кластере Kubernetes. Отчет формируется с использованием инструментов статического анализа, таких как Trivy, и содержит выводы проверок, направленных на выявление нарушений политик безопасности. В частности, анализ позволяет обнаружить роли (как на уровне пространств имен — Role, так и на уровне кластера — ClusterRole), которые:
- предоставляют избыточные привилегии (например, неограниченный доступ к секретам во всех API-группах);
- не соответствуют принципу минимально необходимых прав (least privilege).
Каждый экземпляр RbacAssessmentReport привязан к конкретной роли и размещается в том же пространстве имен, что и проверяемый объект (для ClusterRole отчет создается в неймспейсе по умолчанию или в соответствии с логикой оператора). Имя ресурса формируется по шаблону -<имя_роли>.
Пример:
apiVersion: aquasecurity.github.io/v1alpha1
kind: RbacAssessmentReport
metadata:
name: role-868458b9d6
namespace: kube-system
report:
checks:
- category: Kubernetes Security Check
checkID: KSV051
description: Check whether role permits creating role bindings and associating
to privileged role/clusterrole
messages:
- ""
severity: HIGH
success: true
title: Do not allow role binding creation and association with privileged role/clusterrole
- category: Kubernetes Security Check
checkID: KSV056
description: The ability to control which pods get service traffic directed to
them allows for interception attacks. Controlling network policy allows for
bypassing lateral movement restrictions.
messages:
- ""
severity: HIGH
success: true
title: Do not allow management of networking resources
- category: Kubernetes Security Check
checkID: KSV041
description: Check whether role permits managing secrets
messages:
- Role permits management of secret(s)
severity: CRITICAL
success: false
title: Do not allow management of secrets
- category: Kubernetes Security Check
checkID: KSV047
description: Check whether role permits privilege escalation fr om node proxy
messages:
- ""
severity: HIGH
success: true
title: Do not allow privilege escalation fr om node proxy
- category: Kubernetes Security Check
checkID: KSV045
description: Check whether role permits wildcard verb on specific resources
messages:
- ""
severity: CRITICAL
success: true
title: No wildcard verb roles
- category: Kubernetes Security Check
checkID: KSV054
description: Check whether role permits attaching to shell on pods
messages:
- ""
severity: HIGH
success: true
title: Do not allow attaching to shell on pods
- category: Kubernetes Security Check
checkID: KSV044
description: Check whether role permits wildcard verb on wildcard resource
messages:
- ""
severity: CRITICAL
success: true
title: No wildcard verb and resource roles
- category: Kubernetes Security Check
checkID: KSV050
description: An effective level of access equivalent to cluster-admin should not
be provided.
messages:
- ""
severity: CRITICAL
success: true
title: Do not allow management of RBAC resources
- category: Kubernetes Security Check
checkID: KSV046
description: Check whether role permits specific verb on wildcard resources
messages:
- ""
severity: CRITICAL
success: true
title: No wildcard resource roles
- category: Kubernetes Security Check
checkID: KSV055
description: Check whether role permits allowing users in a rolebinding to add
other users to their rolebindings
messages:
- ""
severity: LOW
success: true
title: Do not allow users in a rolebinding to add other users to their rolebindings
- category: Kubernetes Security Check
checkID: KSV052
description: Check whether role permits creating role ClusterRoleBindings and
association with privileged cluster role
messages:
- ""
severity: HIGH
success: true
title: Do not allow role to create ClusterRoleBindings and association with privileged
role
- category: Kubernetes Security Check
checkID: KSV053
description: Check whether role permits getting shell on pods
messages:
- ""
severity: HIGH
success: true
title: Do not allow getting shell on pods
- category: Kubernetes Security Check
checkID: KSV042
description: Used to cover attacker’s tracks, but most clusters ship logs quickly
off-cluster.
messages:
- ""
severity: MEDIUM
success: true
title: Do not allow deletion of pod logs
- category: Kubernetes Security Check
checkID: KSV049
description: Some workloads leverage configmaps to store sensitive data or configuration
parameters that affect runtime behavior that can be modified by an attacker
or combined with another issue to potentially lead to compromise.
messages:
- ""
severity: MEDIUM
success: true
title: Do not allow management of configmaps
- category: Kubernetes Security Check
checkID: KSV043
description: Check whether role permits impersonating privileged groups
messages:
- ""
severity: CRITICAL
success: true
title: Do not allow impersonation of privileged groups
- category: Kubernetes Security Check
checkID: KSV048
description: Check whether role permits update/create of a malicious pod
messages:
- ""
severity: HIGH
success: true
title: Do not allow update/create of a malicious pod
scanner:
name: Trivy
vendor: Aqua Security
version: '0.22.0'
summary:
criticalCount: 1
highCount: 0
lowCount: 0
mediumCount: 0
updateTimestamp: null
ClusterComplianceReport
Ресурс ClusterComplianceReport предназначен для агрегации данных о соответствии кластера Kubernetes требованиям безопасности. В текущей реализации основной областью проверки является CIS Kubernetes Benchmark — стандартизированный набор рекомендаций по безопасной конфигурации компонентов Kubernetes.
Отчет имеет двухкомпонентную структуру:
- spec.compliance.controls - определяет набор контролируемых критериев и параметров проверки;
- status - содержит фактические результаты выполнения проверок, соответствующих заданным в спецификации критериям. Итоговые данные формируются путем консолидации информации от всех интегрированных сканеров безопасности, работающих в кластере.
Пример:
apiVersion: aquasecurity.github.io/v1alpha1
kind: ClusterComplianceReport
metadata:
name: cis
spec:
compliance:
controls:
- checks:
- id: AVD-KCV-0048
commands:
- id: CMD-0001
description: Ensure that the API server pod specification file has permissions
of 600 or more restrictive
id: 1.1.1
name: Ensure that the API server pod specification file permissions are set
to 600 or more restrictive
severity: HIGH
- checks:
- id: AVD-KCV-0049
commands:
- id: CMD-0002
description: Ensure that the API server pod specification file ownership is
set to root:root
id: 1.1.2
name: Ensure that the API server pod specification file ownership is set to
root:root
severity: HIGH
...
summary:
failCount: 9
passCount: 107
updateTimestamp: "2025-07-29T06:00:00Z"
Настройка использования TLS-сертификатов
Deckhouse Kubernetes Platform (DKP) предоставляет встроенные средства управления TLS-сертификатами, упрощающие настройку и управление шифрованием трафика в приложениях, работающих в кластере.
На этой странице описаны следующие аспекты использования сертификатов в DKP:
- как вручную заказывать TLS-сертификаты с помощью ресурсов Certificate и ClusterIssuer;
- как безопасно хранить и использовать учётные данные для доступа к удостоверяющим центрам (CA);
- как автоматически получать сертификаты с помощью аннотации tls-acme в ресурсах Ingress.
Общее описание порядка управления сертификатами в DKP, список поддерживаемых издателей, а также рекомендации по их настройке приведены на странице «Управление сертификатами».
Работа с сертификатами
Получение информации о сертификатах
- Чтобы вывести список всех сертификатов в кластере, используйте следующую команду:
d8 k get certificate --all-namespaces
- Чтобы проверить статус конкретного сертификата, воспользуйтесь следующей командой:
d8 k -n describe certificate
Автоматический заказ сертификата
Чтобы заказать выпуск сертификата letsencrypt, выполните следующие шаги:
1. Создайте ресурс Certificate, опираясь на документацию cert-manager. Сверяйтесь с примером ниже:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example-com # Имя сертификата.
namespace: default
spec:
secretName: example-com-tls # Имя Secret, в котором будет сохранён приватный ключ и сертификат.
issuerRef:
kind: ClusterIssuer # Данные об издателе сертификата.
name: letsencrypt
commonName: example.com # Основной домен сертификата.
dnsNames: # Опциональные дополнительные домены сертификата (как минимум одно DNS-имя или IP-адрес).
- www.example.com
- admin.example.com
2. Модуль cert-manager автоматически запустит проверку владения доменом (challenge) с использованием метода, указанного в ресурсе ClusterIssuer — например, HTTP-01 или DNS-01.
3. Модуль cert-manager автоматически создаст временный ресурс Ingress для проверки владения доменом. Временный ресурс не влияет на работу основного Ingress-ресурса.
4. После успешной проверки выпущенный сертификат будет сохранён в Secret, указанный в поле secretName.
Если в процессе заказа сертификата выводится ошибка CAA record does not match issuer, проверьте DNS-записи домена, для которого заказывается сертификат. Для использования сертификата letsencrypt у домена должна быть следующая CAA-запись: issue "letsencrypt.org".
Заказ wildcard-сертификата с DNS в Cloudflare
1. Получите GlobalAPIKey и Email:
- зайдите на страницу dash.cloudflare.com/profile;
- ваша почта указана наверху под Email Address;
- для просмотра API-ключа нажмите View напротив Global API Key внизу страницы.
2. Отредактируйте настройки модуля cert-manager, добавив следующую секцию:
settings:
cloudflareGlobalAPIKey: APIkey
cloudflareEmail: some@mail.somedomain
или указав API-токен вместо ключа (рекомендуемый вариант):
settings:
cloudflareAPIToken: some-token
cloudflareEmail: some@mail.somedomain
После этого DKP автоматически создаст ClusterIssuer и Secret для Cloudflare в пространстве имён d8-cert-manager.
3. Создайте ресурс Certificate с проверкой с помощью провайдера Cloudflare. Данная возможность появится только при указании настройки cloudflareGlobalAPIKey и cloudflareEmail в DKP:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: domain-wildcard
namespace: app-namespace
spec:
secretName: tls-wildcard
issuerRef:
name: cloudflare
kind: ClusterIssuer
commonName: "*.domain.com"
dnsNames:
- "*.domain.com"
4. Создайте ресурс Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: domain-wildcard
namespace: app-namespace
spec:
ingressClassName: nginx
rules:
- host: "*.domain.com"
http:
paths:
- backend:
service:
name: svc-web
port:
number: 80
path: /
tls:
- hosts:
- "*.domain.com"
secretName: tls-wildcard
Заказ wildcard-сертификата с DNS в AWS Route53
1. Создайте пользователя с необходимыми правами:
- зайдите на страницу управления политиками и создайте политику со следующими правами:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "route53:GetChange",
"Resource": "arn:aws:route53:::change/*"
},
{
"Effect": "Allow",
"Action": "route53:ChangeResourceRecordSets",
"Resource": "arn:aws:route53:::hostedzone/*"
},
{
"Effect": "Allow",
"Action": "route53:ListHostedZonesByName",
"Resource": "*"
}
]
}
- зайдите на страницу управления пользователями и добавьте пользователя с созданной ранее политикой.
2. Отредактируйте настройки модуля cert-manager, добавив следующую секцию:
settings:
route53AccessKeyID:
route53SecretAccessKey:
После этого Deckhouse автоматически создаст ClusterIssuer и Secret для Route53 в пространстве имён d8-cert-manager.
3. Создайте ресурс Certificate с проверкой с помощью провайдера Route53. Данная возможность появится только при указании настроек route53AccessKeyID и route53SecretAccessKey в DKP:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: domain-wildcard
namespace: app-namespace
spec:
secretName: tls-wildcard
issuerRef:
name: route53
kind: ClusterIssuer
commonName: "*.domain.com"
dnsNames:
- "*.domain.com"
Заказ wildcard-сертификата с DNS в Google
- Создайте ServiceAccount с необходимой ролью:
- зайдите на страницу управления политиками;
- выберите нужный проект и создайте ServiceAccount с желаемым названием, например dns01-solver;
- зайдите в созданный ServiceAccount и создайте ключ, нажав на Добавить ключ. Будет скачан JSON-файл с данными ключа;
- закодируйте полученный файл в строку формата Base64:
base64 project-209317-556c656b81c4.json
2. Сохраните полученную Base64-строку в параметре cloudDNSServiceAccount.
После этого Deckhouse автоматически создаст ClusterIssuer и Secret для CloudDNS в пространстве имён d8-cert-manager.
3. Создайте ресурс Certificate с валидацией через CloudDNS:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: domain-wildcard
namespace: app-namespace
spec:
secretName: tls-wildcard
issuerRef:
name: clouddns
kind: ClusterIssuer
dnsNames:
- "*.domain.com"
Заказ самоподписанного сертификата
Чтобы заказать самоподписанный сертификат, укажите selfsigned в качестве имени издателя в поле issuerRef.name:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example-com # Имя сертификата.
namespace: default
spec:
secretName: example-com-tls # Имя Secret, в котором будет сохранён приватный ключ и сертификат.
issuerRef:
kind: ClusterIssuer # Данные об издателе сертификата.
name: selfsigned
commonName: example.com # Основной домен сертификата.
dnsNames: # Опциональные дополнительные домены сертификата (как минимум одно DNS-имя или IP-адрес).
- www.example.com
- admin.example.com
Создание самоподписанного сертификата
При самостоятельной генерации сертификатов важно корректно заполнить все поля запроса на сертификат, чтобы итоговый сертификат был правильно издан и гарантированно проходил валидацию в различных сервисах.
Важно придерживаться следующих правил:
1. Указывать доменные имена в поле SAN (Subject Alternative Name).
Поле SAN является более современным и распространенным методом указания доменных имен, на которые распространяется сертификат. Некоторые сервисы на данный момент уже не рассматривают поле CN (Common Name) как источник для доменных имен.
2, Корректно заполнять поля keyUsage, basicConstraints, extendedKeyUsage, а именно:
- basicConstraints = CA:FALSE
Данное поле определяет, относится ли сертификат к конечному пользователю (end-entity certificate) или к центру сертификации (CA certificate). CA-сертификат не может использоваться в качестве сертификата сервиса.
- keyUsage = digitalSignature, keyEncipherment
Поле keyUsage ограничивает допустимые сценарии использования данного ключа:
- digitalSignature — позволяет использовать ключ для подписи цифровых сообщений и обеспечения целостности соединения.
- keyEncipherment — позволяет использовать ключ для шифрования других ключей, что необходимо для безопасного обмена данными с помощью TLS (Transport Layer Security).
- extendedKeyUsage = serverAuth
Поле extendedKeyUsage уточняет дополнительные сценарии использования ключа, которые могут требоваться конкретными протоколами или приложениями:
- serverAuth — указывает, что сертификат предназначен для использования на сервере для аутентификации сервера перед клиентом в процессе установления защищенного соединения.
Также рекомендуется:
1. Издать сертификат на срок не более 1 года (365 дней).
Срок действия сертификата влияет на его безопасность. Срок в 1 год позволяет обеспечить актуальность криптографических методов и своевременно обновлять сертификаты в случае возникновения угроз. Также некоторые современные браузеры на текущий момент отвергают сертификаты со сроком действия более 1 года.
2. Использовать стойкие криптографические алгоритмы, например, алгоритмы на основе эллиптических кривых (в т.ч. prime256v1).
Алгоритмы на основе эллиптических кривых (ECC) предоставляют высокий уровень безопасности при меньшем размере ключа по сравнению с традиционными методами, такими как RSA. Это делает сертификаты более эффективными по производительности и безопасными в долгосрочной перспективе.
3. Не указывать домены в поле CN (Common Name).
Исторически поле CN использовалось для указания основного доменного имени, для которого выдается сертификат. Однако современные стандарты, такие как RFC 2818, рекомендуют использовать поле SAN (Subject Alternative Name) для этой цели. Если сертификат распространяется на несколько доменных имен, указанных в поле SAN, то при дополнительном указании одного из доменов в CN в некоторых сервисах может возникнуть ошибка валидации при обращении к домену, не указанному в CN. Если указывать в CN информацию, не относящуюся напрямую к доменным именам (например, идентификатор или имя сервиса), то сертификат также будет распространяться на эти имена, что может быть использовано для вредоносных целей.
Пример создания сертификата
Для генерации сертификата воспользуемся утилитой openssl.
Заполните конфигурационный файл cert.cnf:
[ req ]
default_bits = 2048
default_md = sha256
prompt = no
distinguished_name = dn
req_extensions = req_ext
[ dn ]
C = RU
ST = Moscow
L = Moscow
O = Example Company
OU = IT Department
# CN = Не указывайте поле CN.
[ req_ext ]
subjectAltName = @alt_names
[ alt_names ]
# Укажите все доменные имена.
DNS.1 = example.com
DNS.2 = www.example.com
DNS.3 = api.example.com
# Укажите IP-адреса (если требуется).
IP.1 = 192.0.2.1
IP.2 = 192.0.4.1
[ v3_ca ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
# Параметры эллиптических кривых.
[ ec_params ]
name = prime256v1
2. Сгенерируйте ключ на основе эллиптических кривых:
openssl ecparam -genkey -name prime256v1 -noout -out ec_private_key.pem
3. Создайте запрос на сертификат:
openssl req -new -key ec_private_key.pem -out example.csr -config cert.cnf
4. Сгенерируйте самоподписанный сертификат:
openssl x509 -req -in example.csr -signkey ec_private_key.pem -out example.crt -days 365 -extensions v3_req -extfile cert.cnf
Защита учётных данных
Если вы не хотите хранить учётные данные в конфигурации DKP, можно создать отдельный Secret и ссылаться на него в ресурсе ClusterIssuer.
Для этого выполните следующее:
1. Создайте Secret с ключом доступа:
d8 k apply -f - less-than-sign
2. Создайте ресурс ClusterIssuer со ссылкой на этот Secret:
d8 k apply -f - less-than-sign
3. Закажите сертификаты как обычно, используя созданный ClusterIssuer:
d8 k apply -f - less-than-sign
Поддержка аннотации tls-acme
DKP поддерживает аннотацию kubernetes.io/tls-acme: "true" в ресурсах Ingress. Компонент cert-manager-ingress-shim следит за появлением аннотации и автоматически создаёт ресурсы Certificate в тех же пространствах имён, что и Ingress-ресурсы.
При использовании аннотации ресурс Certificate создается в связке с существующим Ingress-ресурсом. Для подтверждения владения доменом (challenge) не создаётся отдельный Ingress, а вносятся дополнительные записи в существующий. Следовательно, если на основном ресурсе Ingress настроена аутентификация или whitelist, попытка подтверждения окончится неудачей. По этой причине вместо аннотации рекомендуется использовать ресурс Certificate напрямую.
При переходе с аннотации на Certificate удалите ресурс Certificate, который был автоматически создан с аннотацией. Иначе по обоим ресурсам Certificate будет обновляться один Secret, что может привести превышению лимита запросов Let’s Encrypt.
Пример конфигурации ресурса Ingress с аннотацией:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/tls-acme: "true" # Аннотация.
name: example-com
namespace: default
spec:
ingressClassName: nginx
rules:
- host: example.com
http:
paths:
- backend:
service:
name: site
port:
number: 80
path: /
pathType: ImplementationSpecific
- host: www.example.com # Дополнительный домен.
http:
paths:
- backend:
service:
name: site
port:
number: 80
path: /
pathType: ImplementationSpecific
- host: admin.example.com # Ещё один дополнительный домен.
http:
paths:
- backend:
service:
name: site
port:
number: 80
path: /
pathType: ImplementationSpecific
tls:
- hosts:
- example.com
- www.example.com # Дополнительный домен.
- admin.example.com # Ещё один дополнительный домен.
secretName: example-com-tls # Имя для Certificate и Secret.