This commit is contained in:
65
.gitlab-ci.yml.example
Normal file
65
.gitlab-ci.yml.example
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# .gitlab-ci.yml.example
|
||||||
|
# GitLab CI/CD pipeline: build, sign and push container image with Cosign
|
||||||
|
#
|
||||||
|
# Required CI/CD variables (Settings → CI/CD → Variables):
|
||||||
|
# COSIGN_PRIVATE_KEY — contents of cosign.key (type: Variable, masked, protected)
|
||||||
|
# COSIGN_PASSWORD — password for the private key (type: Variable, masked, protected)
|
||||||
|
# REGISTRY_USER — registry login username (type: Variable)
|
||||||
|
# REGISTRY_PASSWORD — registry login password (type: Variable, masked, protected)
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
- sign
|
||||||
|
- verify
|
||||||
|
|
||||||
|
variables:
|
||||||
|
REGISTRY: git.realmanual.ru
|
||||||
|
IMAGE: ${REGISTRY}/${CI_PROJECT_PATH}
|
||||||
|
|
||||||
|
build:
|
||||||
|
stage: build
|
||||||
|
image: docker:27
|
||||||
|
services:
|
||||||
|
- docker:27-dind
|
||||||
|
variables:
|
||||||
|
DOCKER_TLS_CERTDIR: "/certs"
|
||||||
|
before_script:
|
||||||
|
- echo "${REGISTRY_PASSWORD}" | docker login ${REGISTRY} -u ${REGISTRY_USER} --password-stdin
|
||||||
|
- export VERSION=$(cat image/VERSION)
|
||||||
|
script:
|
||||||
|
- docker build -t ${IMAGE}:${CI_COMMIT_SHA} -t ${IMAGE}:${VERSION} image/
|
||||||
|
- docker push ${IMAGE}:${CI_COMMIT_SHA}
|
||||||
|
- docker push ${IMAGE}:${VERSION}
|
||||||
|
# save digest for sign/verify stages
|
||||||
|
- docker inspect --format='{{index .RepoDigests 0}}' ${IMAGE}:${CI_COMMIT_SHA} | cut -d@ -f2 > digest.txt
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- digest.txt
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_BRANCH == "main"
|
||||||
|
|
||||||
|
sign:
|
||||||
|
stage: sign
|
||||||
|
image: alpine:3.20
|
||||||
|
before_script:
|
||||||
|
- apk add --no-cache cosign
|
||||||
|
script:
|
||||||
|
- export IMAGE_DIGEST=$(cat digest.txt)
|
||||||
|
- cosign sign --yes
|
||||||
|
--key env://COSIGN_PRIVATE_KEY
|
||||||
|
${IMAGE}@${IMAGE_DIGEST}
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_BRANCH == "main"
|
||||||
|
|
||||||
|
verify:
|
||||||
|
stage: verify
|
||||||
|
image: alpine:3.20
|
||||||
|
before_script:
|
||||||
|
- apk add --no-cache cosign
|
||||||
|
script:
|
||||||
|
- export IMAGE_DIGEST=$(cat digest.txt)
|
||||||
|
- cosign verify
|
||||||
|
--key keys/cosign.pub
|
||||||
|
${IMAGE}@${IMAGE_DIGEST}
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_BRANCH == "main"
|
||||||
123
README.md
123
README.md
@@ -1,62 +1,105 @@
|
|||||||
# Custom Nginx + Image Signing + Kyverno Policy
|
# Cosign Image Signing + Kyverno Policy
|
||||||
|
|
||||||
## Описание
|
## Описание
|
||||||
|
|
||||||
Демонстрация полного цикла: сборка кастомного nginx-образа, подпись через HashiCorp Vault, деплой в Kubernetes через Helm и валидация подписи Kyverno.
|
Демонстрация полного цикла безопасного деплоя контейнеров: сборка кастомного nginx-образа, криптографическая подпись через Cosign (Sigstore), деплой в Kubernetes через Helm и валидация подписи политикой Kyverno.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Структура проекта
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── .gitea/workflows/
|
||||||
|
│ └── build-sign-push.yaml # CI/CD пайплайн для Gitea Actions
|
||||||
|
├── .gitlab-ci.yml.example # Пример CI/CD пайплайна для GitLab
|
||||||
|
├── helm/nginx/ # Helm чарт для деплоя
|
||||||
|
│ ├── Chart.yaml
|
||||||
|
│ ├── values.yaml
|
||||||
|
│ └── templates/
|
||||||
|
│ ├── _helpers.tpl
|
||||||
|
│ ├── deployment.yaml
|
||||||
|
│ └── service.yaml
|
||||||
|
├── image/
|
||||||
|
│ ├── Dockerfile # Кастомный nginx (non-root, порт 8080)
|
||||||
|
│ ├── nginx.conf
|
||||||
|
│ └── VERSION
|
||||||
|
├── keys/
|
||||||
|
│ ├── cosign.key # Приватный ключ (не в git)
|
||||||
|
│ └── cosign.pub # Публичный ключ для верификации
|
||||||
|
└── policies/
|
||||||
|
└── kyverno-image-signature-policy.yaml # Политика Kyverno
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Как это работает
|
||||||
|
|
||||||
|
1. **Push в main** → Gitea Actions запускает пайплайн
|
||||||
|
2. **Сборка** → Docker-образ собирается и пушится в реестр `git.realmanual.ru`
|
||||||
|
3. **Подпись** → Cosign подписывает образ по digest приватным ключом
|
||||||
|
4. **Верификация в CI** → Cosign проверяет подпись публичным ключом
|
||||||
|
5. **Деплой** → Helm разворачивает образ в namespace `cosign-test`
|
||||||
|
6. **Enforce** → Kyverno перехватывает создание Pod, проверяет подпись образа и блокирует неподписанные
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Шаги запуска
|
## Шаги запуска
|
||||||
|
|
||||||
### 1. Подготовка Vault
|
### 1. Генерация ключей Cosign
|
||||||
|
|
||||||
- Настройте Transit Engine и сгенерируйте ключ `container-sign` в Vault.
|
```sh
|
||||||
- Примените политику из `vault_approle_policy.hcl`:
|
cosign generate-key-pair
|
||||||
```sh
|
```
|
||||||
vault policy write container-sign vault_approle_policy.hcl
|
|
||||||
```
|
|
||||||
- Создайте appRole и получите RoleID/SecretID:
|
|
||||||
```sh
|
|
||||||
sh vault_approle_setup.sh
|
|
||||||
```
|
|
||||||
- Сохраните RoleID и SecretID для переменных CI/CD.
|
|
||||||
|
|
||||||
### 2. Настройка GitLab CI/CD
|
Поместите `cosign.key` и `cosign.pub` в директорию `keys/`.
|
||||||
|
|
||||||
- В настройках проекта добавьте переменные:
|
### 2. Настройка секретов CI/CD
|
||||||
- `VAULT_ROLE_ID` — из Vault
|
|
||||||
- `VAULT_SECRET_ID` — из Vault
|
#### Gitea (Settings → Secrets)
|
||||||
- Убедитесь, что `.gitlab-ci.yml` корректно настроен под ваш реестр и адрес Vault.
|
|
||||||
|
| Секрет | Описание |
|
||||||
|
|--------|----------|
|
||||||
|
| `COSIGN_PRIVATE_KEY` | Содержимое файла `cosign.key` целиком (включая `-----BEGIN/END-----`) |
|
||||||
|
| `COSIGN_PASSWORD` | Пароль от приватного ключа, заданный при `cosign generate-key-pair` |
|
||||||
|
| `PUSH_TOKEN` | Токен доступа к реестру контейнеров |
|
||||||
|
|
||||||
|
#### GitLab (Settings → CI/CD → Variables)
|
||||||
|
|
||||||
|
Для GitLab используйте файл `.gitlab-ci.yml.example` (переименуйте в `.gitlab-ci.yml`).
|
||||||
|
|
||||||
|
| Переменная | Тип | Masked | Protected | Описание |
|
||||||
|
|------------|-----|--------|-----------|----------|
|
||||||
|
| `COSIGN_PRIVATE_KEY` | Variable | Yes | Yes | Содержимое файла `cosign.key` целиком |
|
||||||
|
| `COSIGN_PASSWORD` | Variable | Yes | Yes | Пароль от приватного ключа |
|
||||||
|
| `REGISTRY_USER` | Variable | No | No | Логин для реестра контейнеров |
|
||||||
|
| `REGISTRY_PASSWORD` | Variable | Yes | Yes | Пароль/токен для реестра контейнеров |
|
||||||
|
|
||||||
|
> **Важно:** переменные с флагом **Masked** не отображаются в логах джобов. Флаг **Protected** ограничивает доступ только защищёнными ветками (main).
|
||||||
|
|
||||||
### 3. Сборка и подпись образа
|
### 3. Сборка и подпись образа
|
||||||
|
|
||||||
- Запустите pipeline в GitLab.
|
Пуш в ветку `main` автоматически запустит пайплайн, который соберёт, подпишет и верифицирует образ.
|
||||||
- Образ будет собран, отправлен в реестр и подписан через cosign с использованием Vault.
|
|
||||||
|
|
||||||
### 4. Деплой в Kubernetes
|
### 4. Применение политики Kyverno
|
||||||
|
|
||||||
- Перейдите в директорию helm-чарта:
|
```sh
|
||||||
```sh
|
kubectl apply -f policies/kyverno-image-signature-policy.yaml
|
||||||
cd helm/nginx
|
```
|
||||||
helm install nginx .
|
|
||||||
```
|
|
||||||
- Проверьте, что деплой прошёл успешно.
|
|
||||||
|
|
||||||
### 5. Настройка Kyverno
|
Политика требует подпись для всех образов в namespace `cosign-test`.
|
||||||
|
|
||||||
- Получите публичный ключ из Vault:
|
### 5. Деплой в Kubernetes
|
||||||
```sh
|
|
||||||
vault read -field=public_key transit/keys/container-sign
|
```sh
|
||||||
```
|
helm upgrade -i -n cosign-test cosign-test helm/nginx
|
||||||
- Вставьте ключ в `kyverno-image-signature-policy.yaml` вместо `<VAULT_PUBLIC_KEY>`.
|
```
|
||||||
- Примените политику:
|
|
||||||
```sh
|
|
||||||
kubectl apply -f kyverno-image-signature-policy.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Итог
|
## Требования
|
||||||
|
|
||||||
- Только подписанные образы будут запускаться в кластере.
|
- Kubernetes кластер с установленным Kyverno
|
||||||
- Весь процесс автоматизирован через CI/CD и Vault.
|
- Gitea с поддержкой Actions или GitLab CI/CD
|
||||||
|
- Cosign (sigstore)
|
||||||
|
- Helm 3
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
replicaCount: 1
|
replicaCount: 1
|
||||||
image:
|
image:
|
||||||
repository: git.realmanual.ru/pub/cosign-images
|
repository: git.realmanual.ru/pub/cosign-images
|
||||||
|
# repository: nginx
|
||||||
tag: 0.1.0
|
tag: 0.1.0
|
||||||
pullPolicy: Always
|
pullPolicy: Always
|
||||||
service:
|
service:
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ spec:
|
|||||||
namespaces: ["cosign-test"]
|
namespaces: ["cosign-test"]
|
||||||
verifyImages:
|
verifyImages:
|
||||||
- imageReferences:
|
- imageReferences:
|
||||||
- "git.realmanual.ru/pub/*"
|
# - "git.realmanual.ru/pub/*"
|
||||||
|
- "*"
|
||||||
attestors:
|
attestors:
|
||||||
- entries:
|
- entries:
|
||||||
- keys:
|
- keys:
|
||||||
|
|||||||
Reference in New Issue
Block a user