Внутрішня TLS-комунікація між подами у Kubernetes за допомогою cert-manager¶
Written by:
Igor Gorovyy
DevOps Engineer Lead & Senior Solutions Architect
Чому cert-manager є критично важливим для Kubernetes¶
В сучасному світі DevSecOps і zero-trust архітектури, безпека внутрішньої комунікації між мікросервісами стає не просто рекомендацією, а обов'язковою вимогою. cert-manager вирішує одну з найскладніших проблем в управлінні Kubernetes кластерами — автоматизацію життєвого циклу TLS-сертифікатів.
Основні проблеми, які вирішує cert-manager:¶
- Ручне управління сертифікатами: без автоматизації адміністратори змушені вручну генерувати, поновлювати та розповсюджувати сертифікати
- Простої через прострочені сертифікати: людський фактор часто призводить до несвоєчасного оновлення сертифікатів
- Неконсистентність у безпеці: різні команди можуть використовувати різні підходи до TLS
- Складність масштабування: зі збільшенням кількості сервісів управління сертифікатами стає неконтрольованим
Ключові переваги cert-manager:¶
- Повна автоматизація: автоматичне генерування, поновлення та розподіл сертифікатів
- Підтримка різних Issuer-ів: від самопідписаних до Let's Encrypt і корпоративних CA
- Kubernetes-native: інтеграція через CRD (Custom Resource Definitions)
- Високий рівень безпеки: дотримання найкращих практик TLS
- Зменшення операційних витрат: мінімізація ручної роботи DevOps команд
- Compliance: автоматичне дотримання корпоративних політик безпеки
Цей документ описує, як налаштувати внутрішню TLS або mTLS-комунікацію між подами у Kubernetes, використовуючи самопідписаний сертифікат через cert-manager.
Ключові концепції cert-manager архітектура взіємодії¶
graph TD
A[Certificate] -->|CSR| B[CertificateRequest]
B -->|відправляється до| C[Issuer / ClusterIssuer]
C -->|підписує| D[CertificateRequest]
D -->|випускає| E[Secret]
E -->|містить| F[tls.crt / tls.key / ca.crt]
F -->|монтується до| G[Pod]
C --> H[CA / SelfSigned / ACME]
- Issuer / ClusterIssuer: об'єкти, що визначають джерело сертифікатів (наприклад, SelfSigned, CA, ACME).
Issuer
діє в межах namespace,ClusterIssuer
— глобально. - Certificate: описує бажаний TLS-сертифікат для сервісу. cert-manager автоматично створює сертифікат і розміщує його в Secret.
- CertificateRequest: внутрішній об'єкт, який cert-manager генерує при обробці
Certificate
. Містить CSR-запит. - Secret: об'єкт Kubernetes, де зберігається готовий сертифікат (
tls.crt
,tls.key
,ca.crt
). - SelfSigned / CA Issuer: джерела сертифікатів, які не потребують зовнішніх служб. Підходять для внутрішньої інфраструктури.
- Renewal: автоматичне оновлення сертифікатів до завершення терміну дії (типово за 30 днів).
- Solver (для ACME): механізм підтвердження володіння доменом (HTTP01, DNS01). Не актуальний для SelfSigned/CA.
Алгоритм дій¶
- Створити SelfSigned Issuer для генерації root CA.
- Згенерувати root CA сертифікат, який буде довіреним центром сертифікації.
- Створити CA Issuer на основі root CA.
- Згенерувати TLS сертифікати для кожного сервісу, які будуть підписані CA.
- Підключити сертифікати до Deployment-ів, щоб сервіси могли використовувати TLS.
- Налаштувати HTTPS/HTTP клієнт і сервер з використанням сертифікатів.
- (Опціонально) Налаштувати взаємну автентифікацію (mTLS).
Процесс¶
graph LR
A1[SelfSigned Issuer] --> A2[Root CA Certificate]
A2 --> B1[CA Issuer]
B1 --> C1[service-a TLS cert]
B1 --> C2[service-b TLS cert]
C1 -->|Mounted as Secret| D1[service-a Pod]
C2 -->|Mounted as Secret| D2[service-b Pod]
D1 -->|HTTPS / mTLS| D2
📄 YAML Маніфести¶
1. SelfSigned Issuer¶
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: selfsigned-issuer
namespace: default
spec:
selfSigned: {}
2. Root CA Certificate¶
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: root-ca
namespace: default
spec:
isCA: true
commonName: my-org-root-ca
secretName: root-ca-secret
issuerRef:
name: selfsigned-issuer
kind: Issuer
3. CA Issuer на базі root CA¶
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: ca-issuer
namespace: default
spec:
ca:
secretName: root-ca-secret
4. Сертифікат для service-a
¶
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: service-a-cert
namespace: default
spec:
secretName: service-a-tls
duration: 8760h
renewBefore: 720h
commonName: service-a.default.svc.cluster.local
dnsNames:
- service-a
- service-a.default
- service-a.default.svc
- service-a.default.svc.cluster.local
issuerRef:
name: ca-issuer
kind: Issuer
5. Сертифікат для service-b
¶
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: service-b-cert
namespace: default
spec:
secretName: service-b-tls
duration: 8760h
renewBefore: 720h
commonName: service-b.default.svc.cluster.local
dnsNames:
- service-b
- service-b.default
- service-b.default.svc
- service-b.default.svc.cluster.local
issuerRef:
name: ca-issuer
kind: Issuer
6. Volume у Deployment¶
volumeMounts:
- name: tls-cert
mountPath: /etc/tls
readOnly: true
volumes:
- name: tls-cert
secret:
secretName: service-a-tls
Як це працює 🚀¶
SelfSigned Issuer
генерує CA-сертифікат (root-ca
), що зберігається вroot-ca-secret
.CA Issuer
використовуєroot-ca
для підпису інших сертифікатів.-
Для кожного сервісу cert-manager:
-
створює CSR
- підписує через
ca-issuer
-
зберігає результат у
Secret
(*.tls
), який включаєtls.crt
,tls.key
,ca.crt
-
TLS сертифікат монтується у под через
volume + mountPath
— детальний процес:
Крок 4.1: Визначення Volume у Deployment
volumes:
- name: tls-cert # Ім'я volume
secret:
secretName: service-a-tls # Посилання на Secret з сертифікатом
defaultMode: 0400 # Права доступу (тільки читання для власника)
items: # (Опціонально) вибіркове монтування файлів
- key: tls.crt
path: server.crt
- key: tls.key
path: server.key
- key: ca.crt
path: ca.crt
Крок 4.2: Монтування у контейнер
volumeMounts:
- name: tls-cert # Ім'я volume (має співпадати з volumes.name)
mountPath: /etc/tls # Шлях у файловій системі контейнера
readOnly: true # Обов'язково для безпеки
subPath: "" # (Опціонально) монтування підпапки
Крок 4.3: Результат у контейнері Після монтування у контейнері з'являються файли:
/etc/tls/
├── tls.crt # Публічний сертифікат (PEM format)
├── tls.key # Приватний ключ (PEM format)
└── ca.crt # Сертифікат центру сертифікації
Крок 4.4: Внутрішній механізм Kubernetes
graph TD
A[kubelet] -->|Запит Secret| B[Kubernetes API Server]
B -->|Перевірка RBAC| C[etcd]
C -->|Повертає Secret data| B
B -->|Передає kubelet| A
A -->|Створює tmpfs| D[In-Memory Volume]
D -->|Монтує файли| E[Container Filesystem]
F[Secret Update] -->|Automatic| G[Volume Refresh]
H[Pod Restart] -->|Cleanup| I[tmpfs Cleared]
Крок 4.5: Безпекові аспекти монтування
* tmpfs storage: Сертифікати зберігаються в оперативній пам'яті, не на диску
* Automatic cleanup: При видаленні Pod сертифікати автоматично зникають
* RBAC validation: kubelet перевіряє права ServiceAccount на читання Secret
* Namespace isolation: Secret доступний тільки в межах свого namespace
* File permissions: defaultMode встановлює безпечні права доступу (0400 = read-only для власника)
Крок 4.6: Practical використання у коді
// Go приклад читання сертифіката
cert, err := tls.LoadX509KeyPair("/etc/tls/tls.crt", "/etc/tls/tls.key")
if err != nil {
log.Fatal(err)
}
// Налаштування HTTPS сервера
server := &http.Server{
Addr: ":8443",
TLSConfig: &tls.Config{Certificates: []tls.Certificate{cert}},
}
// Node.js приклад використання сертифікатів
const https = require('https');
const fs = require('fs');
const express = require('express');
const app = express();
// Читання сертифікатів
const options = {
key: fs.readFileSync('/etc/tls/tls.key'),
cert: fs.readFileSync('/etc/tls/tls.crt'),
ca: fs.readFileSync('/etc/tls/ca.crt'),
requestCert: true, // Для mTLS
rejectUnauthorized: true // Строга перевірка сертифікатів
};
app.get('/', (req, res) => {
res.send('Secure HTTPS server with cert-manager certificates!');
});
// Створення HTTPS сервера
https.createServer(options, app).listen(8443, () => {
console.log('HTTPS Server running on port 8443');
});
// Приклад HTTPS клієнта для mTLS
const clientOptions = {
hostname: 'service-b.default.svc.cluster.local',
port: 8443,
path: '/',
method: 'GET',
key: fs.readFileSync('/etc/tls/tls.key'),
cert: fs.readFileSync('/etc/tls/tls.crt'),
ca: fs.readFileSync('/etc/tls/ca.crt')
};
const req = https.request(clientOptions, (res) => {
console.log('Status:', res.statusCode);
res.on('data', (data) => {
console.log(data.toString());
});
});
req.end();
- Додатки використовують сертифікати для запуску HTTPS-серверів або здійснення запитів через HTTPS
- При mTLS: обидві сторони автентифікують одна одну на основі
ca.crt
TLS vs mTLS¶
TLS (односторонній) | mTLS (взаємний) | |
---|---|---|
Сервер має сертифікат | ✅ | ✅ |
Клієнт має сертифікат | ❌ | ✅ |
Перевірка клієнта | ❌ | ✅ |