mirror of
https://gitlab.sarex.io/infra/terraform-contour-mirror.git
synced 2026-08-05 18:31:00 +03:00
feat(yc-s3): use bucket policy instead of folder-level IAM roles
- Remove yandex_resourcemanager_folder_iam_member with storage.editor - Add yandex_storage_bucket_policy for bucket-level access control - SA now has access only to its own bucket via bucket policy - Add role variable (storage.uploader, storage.viewer, storage.editor) - Add README.md with module documentation - Remove comments from module files
This commit is contained in:
parent
6a141f8d51
commit
fb3b9894d4
236
README.md
Normal file
236
README.md
Normal file
@ -0,0 +1,236 @@
|
||||
# Terraform Infrastructure
|
||||
|
||||
Infrastructure as Code для управления ресурсами Yandex Cloud и Kubernetes.
|
||||
|
||||
## Структура
|
||||
|
||||
```
|
||||
terraform/
|
||||
├── infrastructure.yaml # Конфигурация всех ресурсов (единый источник правды)
|
||||
├── live/ # Terragrunt конфигурации по окружениям
|
||||
│ ├── terragrunt.hcl # Общие настройки (backend, providers)
|
||||
│ └── stage/ # Stage окружение
|
||||
│ ├── env.hcl
|
||||
│ ├── namespace/
|
||||
│ ├── database/
|
||||
│ ├── s3/
|
||||
│ └── secrets/
|
||||
└── modules/ # Terraform модули
|
||||
├── k8s-namespace/ # Создание Kubernetes namespaces
|
||||
├── k8s-secret/ # Создание Kubernetes secrets (с зависимостями)
|
||||
├── k8s-secrets/ # Простое создание secrets
|
||||
├── yc-database/ # PostgreSQL users и databases в Yandex Cloud
|
||||
└── yc-s3/ # S3 бакеты с изолированным доступом
|
||||
```
|
||||
|
||||
## Принцип работы
|
||||
|
||||
### infrastructure.yaml
|
||||
|
||||
Единый файл конфигурации для всех ресурсов. Структура:
|
||||
|
||||
```yaml
|
||||
environments:
|
||||
stage: # Имя окружения
|
||||
namespaces: # Kubernetes namespaces
|
||||
- name: pulse
|
||||
labels: {}
|
||||
annotations: {}
|
||||
|
||||
buckets: # S3 бакеты
|
||||
- name: pulse-stage
|
||||
acl: private
|
||||
role: storage.uploader # опционально
|
||||
versioning:
|
||||
enabled: false
|
||||
cors:
|
||||
enabled: true
|
||||
allowed_methods: [GET, PUT]
|
||||
|
||||
databases: # PostgreSQL databases
|
||||
- cluster_id: "xxx"
|
||||
database:
|
||||
name: mydb
|
||||
extensions: [pg_stat_statements]
|
||||
user:
|
||||
name: myuser
|
||||
password_length: 32
|
||||
conn_limit: 10
|
||||
|
||||
secrets: # Kubernetes secrets
|
||||
- name: my-secret
|
||||
namespace: pulse
|
||||
type: database # database | s3 | dockerconfigjson | opaque
|
||||
dependencies:
|
||||
cluster: "xxx"
|
||||
db: mydb
|
||||
user: myuser
|
||||
```
|
||||
|
||||
### Terragrunt
|
||||
|
||||
Terragrunt читает `infrastructure.yaml` и передаёт данные в модули:
|
||||
|
||||
```hcl
|
||||
locals {
|
||||
infra_config = yamldecode(file("${local.repo_root}/infrastructure.yaml"))
|
||||
env_config = local.infra_config.environments[local.env_name]
|
||||
}
|
||||
|
||||
inputs = {
|
||||
buckets = local.env_config.buckets
|
||||
}
|
||||
```
|
||||
|
||||
### Порядок применения
|
||||
|
||||
Модули имеют зависимости и применяются в порядке:
|
||||
|
||||
```
|
||||
namespace → database → s3 → secrets
|
||||
```
|
||||
|
||||
## Модули
|
||||
|
||||
### yc-s3
|
||||
|
||||
Создание S3 бакетов с изолированным доступом.
|
||||
|
||||
**Особенности:**
|
||||
- Каждый бакет получает отдельный Service Account
|
||||
- SA не имеет глобальных IAM ролей на storage
|
||||
- Доступ к бакету только через bucket policy
|
||||
- SA может работать только со своим бакетом
|
||||
|
||||
**Роли:**
|
||||
- `storage.uploader` (default) — PutObject, DeleteObject, GetObject
|
||||
- `storage.viewer` — GetObject, ListBucket
|
||||
- `storage.editor` — все операции
|
||||
|
||||
**Пример:**
|
||||
```yaml
|
||||
buckets:
|
||||
- name: my-bucket
|
||||
acl: private
|
||||
role: storage.uploader # опционально
|
||||
```
|
||||
|
||||
### yc-database
|
||||
|
||||
Создание PostgreSQL users и databases в существующем кластере.
|
||||
|
||||
**Особенности:**
|
||||
- Пароль генерируется автоматически
|
||||
- Поддержка extensions
|
||||
- Поддержка permissions на другие БД
|
||||
- `ignore_changes` на password
|
||||
|
||||
**Пример:**
|
||||
```yaml
|
||||
databases:
|
||||
- cluster_id: "c9qa2coo5ukgcg93fldm"
|
||||
database:
|
||||
name: mydb
|
||||
extensions: [pg_stat_statements]
|
||||
user:
|
||||
name: myuser
|
||||
password_length: 32
|
||||
conn_limit: 10
|
||||
permissions: [other_db]
|
||||
```
|
||||
|
||||
### k8s-secret
|
||||
|
||||
Создание Kubernetes secrets с поддержкой зависимостей.
|
||||
|
||||
**Типы секретов:**
|
||||
- `database` — host, port, database, username, password, ca.crt
|
||||
- `s3` — access_key, secret_key, bucket, endpoint
|
||||
- `dockerconfigjson` — credentials для container registry
|
||||
- `opaque` — произвольные данные
|
||||
|
||||
**Особенности:**
|
||||
- Автоматическое получение данных из database/s3 модулей
|
||||
- Поддержка `random_keys` для генерации паролей
|
||||
- Поддержка `custom_keys` для статических значений
|
||||
- `ignore_changes` для предотвращения перезаписи
|
||||
|
||||
**Пример:**
|
||||
```yaml
|
||||
secrets:
|
||||
- name: my-db-secret
|
||||
namespace: pulse
|
||||
type: database
|
||||
dependencies:
|
||||
cluster: "c9qa2coo5ukgcg93fldm"
|
||||
db: mydb
|
||||
user: myuser
|
||||
random_keys:
|
||||
session_key:
|
||||
length: 32
|
||||
special: false
|
||||
lifecycle:
|
||||
ignore_changes: true
|
||||
```
|
||||
|
||||
### k8s-namespace
|
||||
|
||||
Создание Kubernetes namespaces.
|
||||
|
||||
**Пример:**
|
||||
```yaml
|
||||
namespaces:
|
||||
- name: pulse
|
||||
labels:
|
||||
environment: stage
|
||||
annotations:
|
||||
managed-by: terraform
|
||||
```
|
||||
|
||||
## Использование
|
||||
|
||||
### Локальный запуск
|
||||
|
||||
```bash
|
||||
cd terraform/live/stage/s3
|
||||
terragrunt init
|
||||
terragrunt plan
|
||||
terragrunt apply
|
||||
```
|
||||
|
||||
### Применение всех модулей
|
||||
|
||||
```bash
|
||||
cd terraform/live/stage
|
||||
terragrunt run-all plan
|
||||
terragrunt run-all apply
|
||||
```
|
||||
|
||||
### Переменные окружения
|
||||
|
||||
```bash
|
||||
export YC_TOKEN="..." # IAM токен (или YC_SERVICE_ACCOUNT_KEY_FILE)
|
||||
export YC_CLOUD_ID="..."
|
||||
export YC_FOLDER_ID="..." # Default folder
|
||||
export YC_STAGE_FOLDER_ID="..." # Stage folder (опционально)
|
||||
export KUBECONFIG="~/.kube/config"
|
||||
export KUBE_CONTEXT="stage"
|
||||
export S3_ACCESS_KEY="..." # Для terraform state
|
||||
export S3_SECRET_KEY="..."
|
||||
export DOCKER_REGISTRY_USERNAME="..." # Для dockerconfigjson secrets
|
||||
export DOCKER_REGISTRY_PASSWORD="..."
|
||||
```
|
||||
|
||||
## CI/CD
|
||||
|
||||
Pipeline автоматически:
|
||||
1. Читает изменённые файлы
|
||||
2. Определяет затронутые модули
|
||||
3. Запускает `terragrunt plan` / `terragrunt apply`
|
||||
|
||||
## Требования
|
||||
|
||||
- Terraform >= 1.0
|
||||
- Terragrunt >= 0.45
|
||||
- Yandex Cloud провайдер ~> 0.100
|
||||
- IAM токен с правами `storage.admin` для создания бакетов и bucket policy
|
||||
@ -1,27 +1,20 @@
|
||||
# Универсальный модуль для создания S3 бакетов
|
||||
|
||||
locals {
|
||||
buckets_map = { for bucket in var.buckets : bucket.name => bucket }
|
||||
|
||||
role_actions = {
|
||||
"storage.uploader" = ["s3:PutObject", "s3:DeleteObject", "s3:GetObject"]
|
||||
"storage.viewer" = ["s3:GetObject", "s3:ListBucket"]
|
||||
"storage.editor" = ["s3:*"]
|
||||
}
|
||||
}
|
||||
|
||||
# Создание сервисного аккаунта для каждого бакета
|
||||
resource "yandex_iam_service_account" "sa" {
|
||||
for_each = local.buckets_map
|
||||
|
||||
name = "${each.key}-sa"
|
||||
description = "Service account for ${each.key} bucket"
|
||||
description = "Service account for ${each.key} bucket (access via bucket policy only)"
|
||||
}
|
||||
|
||||
# Назначение роли storage.editor сервисному аккаунту
|
||||
resource "yandex_resourcemanager_folder_iam_member" "storage_editor" {
|
||||
for_each = local.buckets_map
|
||||
|
||||
folder_id = var.folder_id
|
||||
role = "storage.editor"
|
||||
member = "serviceAccount:${yandex_iam_service_account.sa[each.key].id}"
|
||||
}
|
||||
|
||||
# Создание статического ключа доступа
|
||||
resource "yandex_iam_service_account_static_access_key" "sa_key" {
|
||||
for_each = local.buckets_map
|
||||
|
||||
@ -29,14 +22,13 @@ resource "yandex_iam_service_account_static_access_key" "sa_key" {
|
||||
description = "Static access key for ${each.key} bucket"
|
||||
}
|
||||
|
||||
# Создание S3 бакета
|
||||
resource "yandex_storage_bucket" "this" {
|
||||
for_each = local.buckets_map
|
||||
|
||||
bucket = each.key
|
||||
acl = each.value.acl
|
||||
folder_id = var.folder_id
|
||||
|
||||
# Версионирование объектов
|
||||
dynamic "versioning" {
|
||||
for_each = try(each.value.versioning.enabled, false) ? [1] : []
|
||||
content {
|
||||
@ -44,7 +36,6 @@ resource "yandex_storage_bucket" "this" {
|
||||
}
|
||||
}
|
||||
|
||||
# Настройка CORS
|
||||
dynamic "cors_rule" {
|
||||
for_each = try(each.value.cors.enabled, false) ? [1] : []
|
||||
content {
|
||||
@ -56,3 +47,29 @@ resource "yandex_storage_bucket" "this" {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "yandex_storage_bucket_policy" "this" {
|
||||
for_each = local.buckets_map
|
||||
|
||||
bucket = yandex_storage_bucket.this[each.key].bucket
|
||||
|
||||
policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [
|
||||
{
|
||||
Sid = "AllowServiceAccountAccess"
|
||||
Effect = "Allow"
|
||||
Principal = {
|
||||
CanonicalUser = [yandex_iam_service_account.sa[each.key].id]
|
||||
}
|
||||
Action = local.role_actions[coalesce(each.value.role, var.default_bucket_role)]
|
||||
Resource = [
|
||||
"arn:aws:s3:::${each.key}",
|
||||
"arn:aws:s3:::${each.key}/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
depends_on = [yandex_storage_bucket.this]
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
# Для обратной совместимости - первый бакет
|
||||
output "bucket_name" {
|
||||
description = "Name of the first bucket (for backward compatibility)"
|
||||
value = length(var.buckets) > 0 ? yandex_storage_bucket.this[var.buckets[0].name].bucket : ""
|
||||
@ -16,7 +15,6 @@ output "secret_key" {
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
# Новые outputs - map всех бакетов
|
||||
output "buckets" {
|
||||
description = "Map of all buckets (name => data)"
|
||||
value = {
|
||||
|
||||
@ -3,6 +3,7 @@ variable "buckets" {
|
||||
type = list(object({
|
||||
name = string
|
||||
acl = optional(string, "private")
|
||||
role = optional(string, null)
|
||||
versioning = optional(object({
|
||||
enabled = bool
|
||||
}), { enabled = false })
|
||||
@ -21,3 +22,9 @@ variable "folder_id" {
|
||||
description = "Yandex Cloud folder ID"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "default_bucket_role" {
|
||||
description = "Default role for SA on bucket level (storage.uploader, storage.viewer, storage.editor)"
|
||||
type = string
|
||||
default = "storage.uploader"
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user