Unidad 5.2: Automatización de seguridad
Introducción
Usted desplegó TaskBoard en Managed Kubernetes en el Módulo 3, y ahora se comunica con PostgreSQL, Redis, Object Storage y Kafka. Cada una de esas conexiones está protegida por una credencial, y actualmente esas credenciales están dispersas en archivos de tokens, en el estado de Terraform y en almacenes de secretos de CI. En el momento en que un token se filtra o un ingeniero abandona el equipo, usted necesita rotarlo rápidamente y debe hacerlo sin tener que realizar una navegación manual por una consola.
Esta unidad aborda la seguridad como código. Usted automatizará el ciclo de vida completo de los tokens a través de Token Manager, gestionará usuarios y accesos con el modelo de IAM de IONOS CLOUD en Terraform, aplicará reglas de firewall a través de la misma línea de integración continua que aprovisiona sus servidores, e integrará las credenciales de la base de datos en secretos de Kubernetes directamente desde las salidas de Terraform. El modelo de acceso de IONOS CLOUD tiene características específicas: la autorización basada en grupos en lugar de políticas granulares, los NSGs que no se vinculan a equilibradores de carga administrados ni a nodos de Kubernetes, y los tokens que se muestran exactamente una vez. Manejar correctamente esas características es la diferencia entre una plataforma limpia en auditorías y una incidencia a las 2 a.m.
1. Automatización del ciclo de vida de tokens de API
Los tokens Bearer son la forma en que cada llamada a la API, cliente SDK y pipeline de CI se autentican en IONOS CLOUD. Tratarlos como secretos de larga duración pegados en la configuración es el error de seguridad más común en la plataforma. El Token Manager le permite generar tokens por servicio con tiempos de vida acotados y rotarlos de forma programática.
Un token se solicita mediante sus credenciales de contrato y se devuelve como un JWT. El método de Basic Authentication (nombre de usuario y contraseña en cada solicitud) está en proceso de descontinuación y no debe utilizarse en la automatización, por lo que debe estandarizar el uso de tokens Bearer en todas las partes.
1.1 Generación y alcance de tokens
Solicite un token utilizando la API de autenticación. El valor token devuelto es un JWT que luego debe pasar como Authorization: Bearer <token> en las llamadas posteriores a la Cloud API.
# Request a bearer token using contract credentials (one-time, e.g. in a bootstrap step)
curl -s --request GET \
--user "$IONOS_USERNAME:$IONOS_PASSWORD" \
'https://api.ionos.com/auth/v1/tokens/generate' \
| jq -r '.token' > taskboard-ci.token
Cada token incluye un tiempo de vida (TTL) que determina por cuánto tiempo es válido antes de expirar y quedar inactivo. Los valores de TTL disponibles son fijos: 1 hora, 4 horas, 1 día, 7 días, 30 días, 60 días, 90 días, 180 días y 365 días. Seleccione el TTL más corto que un consumidor dado pueda tolerar. Un pipeline de CI que se ejecuta en cada fusión puede funcionar con un token de 7 días rotado semanalmente; un controlador de larga duración puede necesitar 30 días.
Un único usuario puede tener hasta 100 tokens a la vez. Ese límite es lo suficientemente amplio como para asignar un token propio a cada servicio y a cada entorno, que es exactamente lo que se desea: un token por servicio y por entorno, de modo que la revocación de una credencial filtrada no afecte a nada más.
1.2 Rotación sin tiempo de inactividad
El valor del token se muestra exactamente una vez al generarse y no es recuperable después. No existe una llamada para "mostrar el token de nuevo", por lo que su lógica de rotación debe capturar el valor en el momento de la creación y escribirlo directamente en su destino (un secreto de CI, un secreto de Kubernetes) en el mismo paso.
La rotación sigue un patrón de generar y luego revocar, de modo que nunca exista un intervalo en el que no haya un token válido.
#!/usr/bin/env bash
set -euo pipefail
# 1. Generate the new token and capture it immediately (only chance to read it)
NEW_TOKEN=$(curl -s --request GET --user "$IONOS_USERNAME:$IONOS_PASSWORD" \
'https://api.ionos.com/auth/v1/tokens/generate' | jq -r '.token')
# 2. Push it to consumers BEFORE revoking the old one (overlap window)
kubectl create secret generic ionos-api-token \
--from-literal=token="$NEW_TOKEN" \
--namespace taskboard --dry-run=client -o yaml | kubectl apply -f -
# 3. List existing tokens, identify the old one by its jti, then delete it
curl -s --request GET --header "Authorization: Bearer $NEW_TOKEN" \
'https://api.ionos.com/auth/v1/tokens' | jq '.tokens[] | {id: .id, expirationDate}'
La eliminación de un token en Token Manager lo desactiva de inmediato, incluso si su TTL aún no ha expirado, por lo que la credencial anterior deja de ser válida en el momento en que se invoca DELETE. Ejecute este script según un horario (una tarea cron de CI o un CronJob de Kubernetes) y la rotación se convertirá en una propiedad de la plataforma en lugar de una tarea que alguien debe recordar realizar.
2. IAM como código
El acceso de usuarios y grupos en IONOS CLOUD sigue un modelo de control de acceso basado en roles (Role-Based Access Control) construido sobre grupos, no sobre documentos de política por recurso. Asigna privilegios a un grupo, agrega usuarios al grupo y comparte recursos específicos con el grupo. No existe un lenguaje de política granular por acción, por lo que debe diseñar su esquema de acceso en torno a grupos desde el inicio.
Gestionar esto con Terraform permite que su mapa de acceso sea revisable en solicitudes de extracción (pull requests) y reproducible entre contratos.
2.1 Usuarios, grupos y privilegios
Un grupo posee un conjunto de privilegios a nivel de contrato. Los privilegios de grupo disponibles constituyen un catálogo fijo: Create Data Center, Create Snapshots, Reserve IP Blocks, Create Internet Access, Use Object Storage, Create Backup Units, Create Kubernetes Clusters y Access Activity Log. Habilite únicamente los privilegios que el grupo necesita y nada más.
resource "ionoscloud_group" "taskboard_deployers" {
name = "taskboard-deployers"
create_datacenter = false
create_snapshot = false
reserve_ip = false
access_activity_log = true
create_k8s_cluster = true
s3_privilege = true # Use Object Storage
user_ids = [ionoscloud_user.ci_bot.id]
}
resource "ionoscloud_user" "ci_bot" {
first_name = "TaskBoard"
last_name = "CI"
email = "ci-bot@taskboard.example"
password = var.ci_bot_password # sensitive, from a secret store
administrator = false
force_sec_auth = false
}
Mantenga administrator = false para cada usuario de automatización. Un administrador omite por completo los privilegios de grupo, lo cual invalida el modelo de privilegios mínimos que está construyendo.
2.2 Compartir recursos con grupos
Los privilegios controlan lo que un grupo puede crear. El compartir recursos controla lo que un grupo puede ver y en lo que puede actuar para recursos que ya existen. Los tipos de recursos controlables son centros de datos virtuales, Snapshots, imágenes, bloques de IP, unidades de copia de seguridad y clústeres de Kubernetes. Para cada recurso compartido, un grupo recibe un nivel de autorización: lectura (implícito en cuanto se asigna un recurso al grupo), edición y compartir.
resource "ionoscloud_share" "taskboard_vdc_share" {
group_id = ionoscloud_group.taskboard_deployers.id
resource_id = ionoscloud_datacenter.taskboard.id
edit_privilege = true
share_privilege = false
}
La siguiente tabla asocia los tres niveles de autorización con lo que un miembro del grupo puede hacer, lo cual es la decisión que se toma en cada compartición:
| Nivel | Concedido por | El miembro puede |
|---|---|---|
| Lectura | Implícito cuando el recurso se comparte | Ver el recurso |
| Edición | edit_privilege = true |
Modificar el recurso |
| Compartición | share_privilege = true |
Volver a compartir el recurso con otros grupos |
Conceda Sharing con moderación. Un grupo que puede volver a compartir recursos puede ampliar el acceso más allá de lo que su Terraform describe, creando una divergencia entre su código y la realidad.
3. Automatización de NSG en la línea de integración
Los Network Security Groups son firewalls con estado que se aplican a nivel de VM o NIC. Gestionar sus reglas en Terraform, en lugar de editarlas manualmente, significa que la postura de red se encuentra en la misma línea de integración que los servidores que protege y se revisa en cada cambio.
Un NSG tiene como valor predeterminado la denegación total: el tráfico se descarta a menos que una regla lo permita explícitamente. Las reglas admiten tanto las direcciones INGRESS y EGRESS como un amplio conjunto de protocolos, que incluye TCP, UDP, ICMP, ICMPv6, GRE, VRRP, ESP y AH.
3.1 Reglas como código
Asigne un NSG a un servidor (cubriendo todas sus NIC) o a una NIC individual para un control granular, y luego defina las reglas. La plataforma le limita a 100 reglas por NSG, 10 NSG por NIC y 200 NSG por VDC, lo cual es suficiente para que encontrará un problema de diseño antes que un límite de cuota.
resource "ionoscloud_nsg" "taskboard_app" {
name = "taskboard-app-tier"
description = "App tier: allow HTTPS in, Postgres out"
datacenter_id = ionoscloud_datacenter.taskboard.id
}
resource "ionoscloud_nsg_firewallrule" "allow_https_in" {
nsg_id = ionoscloud_nsg.taskboard_app.id
protocol = "TCP"
name = "https-ingress"
type = "INGRESS"
port_range_start = 443
port_range_end = 443
}
resource "ionoscloud_nsg_firewallrule" "allow_pg_out" {
nsg_id = ionoscloud_nsg.taskboard_app.id
protocol = "TCP"
name = "postgres-egress"
type = "EGRESS"
port_range_start = 5432
port_range_end = 5432
}
# Bind the NSG to the app server's NIC
resource "ionoscloud_nic" "app_nic" {
datacenter_id = ionoscloud_datacenter.taskboard.id
server_id = ionoscloud_server.app.id
lan = ionoscloud_lan.app_lan.id
security_groups_ids = [ionoscloud_nsg.taskboard_app.id]
}
El recurso de API subyacente es security-group, y cada regla expone propiedades como name, protocol, sourceIp, targetIp, portRangeStart, portRangeEnd, icmpType y icmpCode. Los campos ICMP solo se aplican cuando el protocolo es ICMP o ICMPv6.
3.2 Lo que las NSG no cubren
Esta es la restricción que cuesta a los desarrolladores horas de depuración. Las NSG se aplican únicamente a nivel de NIC de servidor VDC. No se vinculan a Managed Application Load Balancer, Managed Network Load Balancer ni Managed Kubernetes. Específicamente, los nodos de los grupos de nodos de Managed Kubernetes y los Cubes suspendidos quedan excluidos de la aplicación de NSG.
# WRONG: there is no security_groups argument on a managed load balancer.
# resource "ionoscloud_application_loadbalancer" "alb" {
# security_groups_ids = [ionoscloud_nsg.x.id] # not a valid argument
# }
Para un servicio con un ALB como front-end, como la API de TaskBoard, no es posible aplicar un firewall al propio ALB mediante un NSG. Proteja la capa colocando los servidores de aplicación en una LAN privada y aplicando reglas de NSG a sus NIC para que solo la subred del ALB pueda acceder a ellos. En el caso de Kubernetes, aplique la política de tráfico mediante objetos Kubernetes NetworkPolicy dentro del clúster, ya que las NIC de los nodos están fuera del alcance de los NSG. Los NSG también son independientes del firewall por NIC heredado, por lo que no espere que uno herede las reglas del otro.
4. Secretos de Kubernetes desde Terraform
Los pods de TaskBoard necesitan la cadena de conexión de PostgreSQL, la contraseña de Redis y la clave de acceso de Object Storage. Ninguno de esos valores debe aparecer nunca en un manifiesto comprometido. El patrón limpio es obtenerlos desde las salidas de Terraform (marcadas como sensibles) y crear secretos de Kubernetes a partir de esas salidas en el momento de la implementación.
4.1 Salidas sensibles que alimentan secretos
Marque cada salida de credenciales como sensitive = true para que Terraform nunca la imprima en los registros ni en terraform output sin la bandera explícita.
output "pg_connection_uri" {
value = "postgresql://${ionoscloud_pg_cluster.taskboard.credentials[0].username}:${var.pg_password}@${ionoscloud_pg_cluster.taskboard.dns_name}:5432/taskboard?sslmode=require"
sensitive = true
}
output "s3_access_key" {
value = ionoscloud_s3_key.taskboard.id
sensitive = true
}
output "s3_secret_key" {
value = ionoscloud_s3_key.taskboard.secret_key
sensitive = true
}
En el momento de la implementación, lea esas salidas y cree el secreto de Kubernetes en un solo paso para que el valor en texto plano nunca se escriba en el disco en un manifiesto:
kubectl create secret generic taskboard-db \
--namespace taskboard \
--from-literal=DATABASE_URL="$(terraform output -raw pg_connection_uri)" \
--from-literal=S3_ACCESS_KEY="$(terraform output -raw s3_access_key)" \
--from-literal=S3_SECRET_KEY="$(terraform output -raw s3_secret_key)" \
--dry-run=client -o yaml | kubectl apply -f -
El idiomático --dry-run=client -o yaml | kubectl apply -f - hace que la operación sea idempotente: al volver a ejecutarla, se actualiza el secreto en su ubicación original en lugar de fallar porque ya existe.
4.2 Consumo y rotación de secretos
La implementación hace referencia al secreto por nombre, por lo que rotar una credencial implica recrear el secreto y reiniciar los pods, nunca editar un manifiesto.
spec:
containers:
- name: taskboard-api
image: taskboard-prod.cr.de-fra.ionos.com/taskboard/api:abc123
envFrom:
- secretRef:
name: taskboard-db
Después de rotar la credencial subyacente y volver a aplicar el secreto, fuerce a los pods a que lo reciban:
kubectl rollout restart deployment/taskboard-api -n taskboard
Para flotas de mayor tamaño, un operador de secretos externo puede sincronizarse desde un almacén central según un horario, pero el patrón de salida de Terraform a secreto es la línea base adecuada y mantiene la fuente de verdad de la credencial en su código de infraestructura.
5. Automatización de claves SSH
Los servidores aprovisionados por Terraform obtienen su acceso SSH a partir de claves inyectadas durante el arranque. SSH Key Manager almacena claves por usuario (hasta 100 claves guardadas por usuario), lo que permite a un equipo hacer referencia al mismo conjunto de claves en diferentes despliegues, y cloud-init las inyecta en las nuevas instancias.
5.1 Inyección y rotación de claves
Guarde las claves públicas del equipo e inyéctelas a través de cloud-init del servidor user_data. La inyección de claves ad hoc en el momento del aprovisionamiento se admite a través de la API de Cloud, que es exactamente lo que utiliza Terraform.
locals {
team_keys = [
file("${path.module}/keys/alice.pub"),
file("${path.module}/keys/bob.pub"),
]
}
resource "ionoscloud_server" "app" {
name = "taskboard-app"
datacenter_id = ionoscloud_datacenter.taskboard.id
cores = 4
ram = 8192
ssh_key_path = local.team_keys
volume {
name = "app-boot"
size = 20
disk_type = "SSD"
image_name = "ubuntu:latest"
}
}
Rotar una clave de equipo es un cambio de código: eliminar la .pub antigua, agregar la nueva y terraform apply. Los servidores nuevos y reprovisionados recogen el cambio de inmediato. Combine esto con un paso de cloud-init que elimine las claves obsoletas de ~/.ssh/authorized_keys en los hosts existentes para que la rotación también alcance a los servidores en ejecución.
#cloud-config
ssh_authorized_keys:
- ssh-ed25519 AAAA... alice@taskboard
- ssh-ed25519 AAAA... bob@taskboard
Mantenga las claves privadas fuera del estado de Terraform y fuera de Git por completo. Solo las claves públicas deben estar en su repositorio; las claves privadas correspondientes deben permanecer con cada ingeniero o en un almacén de secretos dedicado.
Tarjeta rápida de referencia de API
Puntos finales clave para la automatización de tokens y de acceso:
| Método | Punto final | Descripción |
|---|---|---|
GET |
/auth/v1/tokens/generate |
Generar un nuevo token de portador |
GET |
/auth/v1/tokens |
Listar tokens activos |
DELETE |
/auth/v1/tokens/{tokenId} |
Revocar un token de inmediato |
POST |
/cloudapi/v6/um/groups |
Crear un grupo de IAM |
POST |
/cloudapi/v6/datacenters/{dcId}/securitygroups |
Crear un Network Security Group |
URL base: https://api.ionos.com (Recursos de la API de la nube bajo /cloudapi/v6)
Autenticación: Authorization: Bearer <token>
Laboratorio de código
Objetivo: Rotar el token de API de TaskBoard a través del Token Manager, agregar una regla de NSG mediante Terraform e integrar una credencial de base de datos en un secreto de Kubernetes a partir de una salida de Terraform.
Requisitos previos:
- Cuenta de IONOS CLOUD con credenciales de contrato y un VDC de TaskBoard existente
- Terraform con el proveedor
ionoscloudconfigurado kubectlconectado a su clúster MKS de TaskBoard,jqinstalado
Paso 1: Generar un token nuevo
NEW_TOKEN=$(curl -s --request GET --user "$IONOS_USERNAME:$IONOS_PASSWORD" \
'https://api.ionos.com/auth/v1/tokens/generate' | jq -r '.token')
echo "${NEW_TOKEN:0:12}..."
Salida esperada:
eyJ0eXAiOiJK...
Paso 2: Liste sus tokens activos
curl -s --request GET --header "Authorization: Bearer $NEW_TOKEN" \
'https://api.ionos.com/auth/v1/tokens' | jq '.tokens | length'
Salida esperada:
3
**Paso 3: Agregar una regla de tráfico entrante de NSG en Terraform
resource "ionoscloud_nsg_firewallrule" "lab_https" {
nsg_id = ionoscloud_nsg.taskboard_app.id
protocol = "TCP"
name = "lab-https"
type = "INGRESS"
port_range_start = 443
port_range_end = 443
}
terraform apply -target=ionoscloud_nsg_firewallrule.lab_https
Salida esperada:
ionoscloud_nsg_firewallrule.lab_https: Creation complete
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Paso 4: Crear el secreto de K8s a partir de una salida sensible
kubectl create secret generic taskboard-db -n taskboard \
--from-literal=DATABASE_URL="$(terraform output -raw pg_connection_uri)" \
--dry-run=client -o yaml | kubectl apply -f -
Salida esperada:
secret/taskboard-db configured
Paso 5: Verificar el secreto sin exponerlo
kubectl get secret taskboard-db -n taskboard -o jsonpath='{.data.DATABASE_URL}' | base64 -d | sed 's/:[^@]*@/:****@/'
Salida esperada:
postgresql://taskboard:****@pg-xxxx.de-fra.ionos.com:5432/taskboard?sslmode=require
Paso 6: Reiniciar los pods para que adopten el secreto rotado
kubectl rollout restart deployment/taskboard-api -n taskboard
kubectl rollout status deployment/taskboard-api -n taskboard
Salida esperada:
deployment "taskboard-api" successfully rolled out
Paso 7: Revocar el token anterior
curl -s --request DELETE --header "Authorization: Bearer $NEW_TOKEN" \
"https://api.ionos.com/auth/v1/tokens/$OLD_TOKEN_ID" -o /dev/null -w "%{http_code}\n"
Salida esperada:
200
Lista de verificación:
- [ ] Se generó un nuevo token y el token anterior devuelve 401 después de la eliminación
- [ ] La regla de NSG es visible mediante
terraform state show ionoscloud_nsg_firewallrule.lab_https - [ ] Los Pods están en ejecución con el secreto rotado, sin credenciales en ningún manifiesto
Limpieza:
terraform destroy -target=ionoscloud_nsg_firewallrule.lab_https
kubectl delete secret taskboard-db -n taskboard
Errores comunes
-
Intentar leer el valor de un token una segunda vez
- Problema: Su script de rotación genera un token, registra "rotado" y luego intenta recuperar el valor del token para enviarlo a algún lugar, pero solo obtiene metadatos.
- Por qué ocurre: El valor del token se muestra exactamente una vez en el momento de la generación y no es recuperable. La lista de tokens devuelve identificadores y fechas de expiración, nunca el secreto.
- Solución: Capture el valor en el momento de la creación y escríbalo en su destino en la misma etapa:
NEW_TOKEN=$(curl -s --request GET --user "$U:$P" \ 'https://api.ionos.com/auth/v1/tokens/generate' | jq -r '.token') kubectl create secret generic ionos-api-token --from-literal=token="$NEW_TOKEN" \ --dry-run=client -o yaml | kubectl apply -f - -
Adjuntar un NSG a un equilibrador de carga administrado o a un grupo de nodos de MKS
- Problema: Se agrega
security_groups_idsa un ALB o se espera que las reglas de NSG filtren el tráfico de nodos de Kubernetes, y no se aplica ninguna restricción. - Por qué ocurre: Los NSG se aplican únicamente a nivel de NIC de servidor en el VDC. Los ALB/NLB administrados están fuera del alcance, y los nodos de los grupos de nodos de Managed Kubernetes están excluidos explícitamente de la aplicación de NSG.
- Solución: Aplique las reglas de NSG a las NIC de los servidores de aplicaciones ubicados detrás del equilibrador de carga, y utilice objetos NetworkPolicy de Kubernetes dentro del clúster para el control de tráfico a nivel de pod.
- Problema: Se agrega
-
Comprometer credenciales porque la salida no se marcó como sensible
- Problema: Un compañero de equipo ejecuta
terraform outputen los registros de CI y la contraseña de la base de datos se imprime en texto plano en la salida de compilación. - Por qué ocurre: Una salida sin
sensitive = truese renderiza en la salida de la consola y en los registros. - Solución: Marque todas las salidas de credenciales como sensibles y léalas explícitamente con
-rawsolo en el punto de uso:
output "pg_connection_uri" { value = local.pg_uri sensitive = true } - Problema: Un compañero de equipo ejecuta
Resumen
Ahora puede tratar la seguridad como parte de su pipeline de infraestructura, en lugar de una tarea manual en la consola. Los tokens se generan con TTL acotados, con ámbito por servicio y por entorno, y se rotan mediante una secuencia de generación, envío y revocación que nunca deja un vacío. El acceso se describe como código a través de grupos, usuarios y compartidos, lo cual coincide con el modelo de RBAC basado en grupos de IONOS CLOUD. Las reglas de Firewall se distribuyen junto con los servidores que protegen, y las credenciales fluyen desde las salidas sensibles de Terraform directamente hacia los secretos de Kubernetes, sin tocar nunca un archivo comprometido.
Las particularidades específicas de IONOS CLOUD son lo que mantiene esto limpio: tokens mostrados exactamente una vez, un catálogo fijo de privilegios de grupo en lugar de políticas de formato libre, y NSGs que se detienen en la NIC del servidor y nunca alcanzan los equilibradores de carga administrados ni los nodos de Kubernetes. Construya en torno a esas particularidades y su plataforma se mantendrá auditable y recuperable.
Puntos clave:
- Genere un token bearer por servicio y por entorno (hasta 100 por usuario) con el TTL más corto viable del conjunto fijo (1 hora a 365 días)
- Los valores de los tokens se muestran exactamente una vez y la eliminación de un token lo desactiva de inmediato, por lo que la secuencia segura de rotación es capturar, enviar y luego revocar
- El acceso en IONOS CLOUD es RBAC basado en grupos: asigne privilegios a grupos, comparta recursos en niveles de lectura, edición y compartición, y mantenga a los usuarios de automatización como no administradores
- Los NSGs son con estado, con denegación total por defecto, se aplican solo a nivel de NIC del servidor y NO cubren los ALB/NLB administrados ni los nodos de Managed Kubernetes
- Origne los secretos de Kubernetes desde las salidas de Terraform
sensitivepara que las credenciales nunca residan en un manifiesto o en Git
Terminología importante:
- Token Manager: La función de IONOS CLOUD que genera, lista y revoca tokens bearer (JWT) utilizados para la autenticación de API y SDK.
- TTL (Time To Live): El período de validez fijo asignado a un token en la creación, tras el cual expira y se vuelve inactivo.
- RBAC basado en grupos: El modelo de acceso de IONOS CLOUD en el que los privilegios y las comparticiones de recursos se otorgan a grupos en lugar de a usuarios individuales o a políticas por acción.
- Network Security Group (NSG): Un firewall con estado, con denegación total por defecto, adjunto a nivel de VM o NIC, gestionado a través del recurso de API
security-group. - Salida sensible: Una salida de Terraform marcada como
sensitive = truepara que su valor se excluya de la salida de la consola y de los registros.
Próximos pasos
Continuar aprendiendo: Unidad 5.3: GitOps and Deployment Operations
Temas relacionados: