Saltearse al contenido

Operaciones de Seguridad

Tabla de Contenidos

  1. Propósito
  2. Configuración de Azure Key Vault
  3. Gestión de Secrets de Kubernetes
  4. Gestión de Certificados TLS
  5. Autenticación y Autorización
  6. Seguridad de Contenedores e Imágenes
  7. Seguridad de Red
  8. Mejores Prácticas de Seguridad

Propósito

Este documento describe las operaciones de seguridad para la plataforma Algesta, incluyendo gestión de secrets con Azure Key Vault, secrets de Kubernetes, certificados TLS vía cert-manager, autorización basada en JWT/RBAC y prácticas de seguridad de contenedores.


Azure Key Vault Configuration

Key Vault: akv-algesta-{environment} (per-environment)

Terraform Configuration (from modules/secrets/main.tf):

resource "azurerm_key_vault" "this" {
name = var.name
location = var.location
resource_group_name = var.resource_group_name
tenant_id = var.tenant_id
sku_name = "standard"
purge_protection_enabled = false # Enable for production
soft_delete_retention_days = 7
enabled_for_disk_encryption = true
enable_rbac_authorization = true
}

Stored Secrets:

  • mongodb-uri: MongoDB Atlas connection string
  • jwt-secret: JWT signing key for authentication
  • api-keys: External service API keys (SendGrid, Twilio, etc.)
  • tls-certificates: Manual TLS certificates (if not using cert-manager)

Access Control (RBAC):

Ventana de terminal
# Grant Key Vault Secrets Officer role (read/write)
az role assignment create \
--assignee <service-principal-id> \
--role "Key Vault Secrets Officer" \
--scope /subscriptions/{subscription-id}/resourceGroups/rg-algesta-production/providers/Microsoft.KeyVault/vaults/akv-algesta-production
# Grant Key Vault Secrets User role (read-only)
az role assignment create \
--assignee <managed-identity-id> \
--role "Key Vault Secrets User" \
--scope /subscriptions/{subscription-id}/resourceGroups/rg-algesta-production/providers/Microsoft.KeyVault/vaults/akv-algesta-production

Accessing Secrets:

Ventana de terminal
# Via Azure CLI
az keyvault secret show --name mongodb-uri --vault-name akv-algesta-production --query value -o tsv
# Set secret
az keyvault secret set --name jwt-secret --vault-name akv-algesta-production --value "supersecurejwtkey12345"

Future: AKS Integration with CSI Driver:

Ventana de terminal
# Enable Azure Key Vault Provider for Secrets Store CSI Driver
az aks enable-addons --addons azure-keyvault-secrets-provider \
--resource-group rg-algesta-production \
--name aks-algesta-production

Kubernetes Secrets Management

Current Approach: Manual kubectl create secret commands

Production Secrets:

Ventana de terminal
# MongoDB credentials
kubectl create secret generic mongodb-credentials \
--from-literal=uri="mongodb+srv://admin:SecurePassword@cluster.mongodb.net/algesta?retryWrites=true&w=majority" \
--namespace=production
# JWT secret
kubectl create secret generic jwt-credentials \
--from-literal=secret="supersecurejwtkey12345" \
--namespace=production
# Verify
kubectl get secrets -n production

Best Practices:

  • Never commit secrets to Git repositories
  • Use separate secrets per environment
  • Rotate secrets regularly (quarterly recommended)
  • Use Azure Key Vault for centralized secret storage (future Implementación)
  • Enable RBAC to restrict secret access

Decoding Secrets (for debugging only):

Ventana de terminal
# View secret value (base64 encoded)
kubectl get secret mongodb-credentials -n production -o yaml
# Decode value
kubectl get secret mongodb-credentials -n production -o jsonpath='{.data.uri}' | base64 -d

Rotating Secrets:

Ventana de terminal
# Update existing secret
kubectl create secret generic mongodb-credentials \
--from-literal=uri="<new-mongodb-uri>" \
--namespace=production \
--dry-run=client -o yaml | kubectl apply -f -
# Restart deployments to pick up new secret
kubectl rollout restart deployment -n production

TLS Certificate Management

Tool: cert-manager + Let’s Encrypt

ClusterIssuer Configuration (ops-algesta/resources-k8s/cert-manager/Issuer.yaml):

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
namespace: cert-manager
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: j.leon@tresastronautas.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
ingressClassName: nginx

ClusterIssuer for Web App Routing (ops-algesta/resources-k8s/cert-manager/Issuer-webapprouting.yaml):

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod-webapprouting
namespace: cert-manager
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: j.leon@tresastronautas.com
privateKeySecretRef:
name: letsencrypt-prod-webapprouting
solvers:
- http01:
ingress:
ingressClassName: webapprouting.kubernetes.azure.com

Note: The Web App Routing issuer (letsencrypt-prod-webapprouting) is the one currently used in production, as all ingresses use the webapprouting.kubernetes.azure.com ingress class.

Certificate Lifecycle:

  1. Ingress created with cert-manager.io/cluster-issuer annotation
  2. cert-manager creates Certificate resource
  3. ACME challenge (HTTP-01) validates domain ownership
  4. Certificate issued and stored in Kubernetes Secret
  5. Auto-renewal 30 days before expiration

Checking Certificate Estado:

Ventana de terminal
# List certificates
kubectl get certificates -n production
# View certificate details
kubectl describe certificate algesta-api-prod-tls -n production
# Check expiration date
kubectl get secret algesta-api-prod-tls -n production -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -enddate

Manual Certificate Renewal:

Ventana de terminal
# Delete certificate to trigger re-issuance
kubectl delete certificate algesta-api-prod-tls -n production
# cert-manager will automatically recreate
kubectl get certificate algesta-api-prod-tls -n production -w

Troubleshooting Certificate Issues:

Ventana de terminal
# Check cert-manager logs
kubectl logs -n cert-manager -l app=cert-manager --tail=50
# Check certificate order status
kubectl get certificaterequests -n production
kubectl get orders -n production
kubectl get challenges -n production

Authentication and Authorization

JWT-Based Authentication

Implementación in NestJS Microservicios:

JWT Strategy (src/auth/jwt.strategy.ts):

import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { ExtractJwt, Strategy } from "passport-jwt";
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: process.env.JWT_SECRET,
});
}
async validate(payload: any) {
return { userId: payload.sub, email: payload.email, roles: payload.roles };
}
}

JWT Guard (src/auth/jwt-auth.guard.ts):

import { Injectable } from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";
@Injectable()
export class JwtAuthGuard extends AuthGuard("jwt") {}

Protected Endpoint:

import { Controller, Get, UseGuards } from "@nestjs/common";
import { JwtAuthGuard } from "./auth/jwt-auth.guard";
@Controller("orders")
export class OrdersController {
@Get()
@UseGuards(JwtAuthGuard)
findAll() {
return this.ordersService.findAll();
}
}

Role-Based Access Control (RBAC)

Roles Guard (src/auth/roles.guard.ts):

import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.get<string[]>(
"roles",
context.getHandler()
);
if (!requiredRoles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
return requiredRoles.some((role) => user.roles?.includes(role));
}
}

Admin-Only Endpoint:

import { Controller, Delete, UseGuards } from "@nestjs/common";
import { Roles } from "./auth/roles.decorator";
import { RolesGuard } from "./auth/roles.guard";
import { JwtAuthGuard } from "./auth/jwt-auth.guard";
@Controller("orders")
export class OrdersController {
@Delete(":id")
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles("admin")
remove(@Param("id") id: string) {
return this.ordersService.remove(id);
}
}

Kubernetes RBAC

ServiceAccount for CI/CD (ops-algesta/resources-k8s/ServiceAccount/sa-token-azure-devops.yaml):

apiVersion: v1
kind: Secret
metadata:
name: azure-devops-sa-token
namespace: connect-devops
annotations:
kubernetes.io/service-account.name: azure-devops-sa
type: kubernetes.io/service-account-token

Role and RoleBinding (example):

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployment-manager
namespace: production
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: azure-devops-deployment-manager
namespace: production
subjects:
- kind: ServiceAccount
name: azure-devops-sa
namespace: connect-devops
roleRef:
kind: Role
name: deployment-manager
apiGroup: rbac.authorization.k8s.io

Container and Image Security

Image Scanning (Azure Container Registry)

Enable Vulnerability Scanning:

Ventana de terminal
# Enable Microsoft Defender for Cloud for ACR
az security pricing create \
--name ContainerRegistry \
--tier Standard
# Scan specific image
az acr task run --registry acralgestaproduction \
--cmd "mcr.microsoft.com/azure-cli az acr check-health --name acralgestaproduction"

Image Pull Policy:

spec:
containers:
- name: api-gateway
image: acralgestaproduction.azurecr.io/api-gateway:12345
imagePullPolicy: Always # Always pull to get latest security patches

Dockerfile Security Best Practices

Implemented in algesta-api-gateway-nestjs/Dockerfile:

# Multi-stage build (reduces attack surface)
FROM node:20-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Final stage: minimal image
FROM node:20-slim
WORKDIR /app
# Run as non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/main"]

Security Improvements:

  • Use slim base images (reduces vulnerabilities)
  • Multi-stage builds (exclude build tools from final image)
  • Run as non-root user (principle of least privilege)
  • Pin base image versions (node:20-slimnode:20.10-slim)
  • Scan images regularly (Microsoft Defender, Trivy, Snyk)

Dependency Scanning

npm audit (CI/CD integration):

Ventana de terminal
# Run during build stage
npm audit --production
# Fix vulnerabilities automatically
npm audit fix

Snyk (recommended for production):

Ventana de terminal
# Install Snyk CLI
npm install -g snyk
# Authenticate
snyk auth
# Test for vulnerabilities
snyk test
# Monitor project
snyk monitor

Network Security

Network Policies (Future Implementación)

Example: Restrict Inter-Namespace Communication:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-from-monitoring
namespace: production
spec:
podSelector:
matchLabels:
app: api-gateway
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: monitoring
ports:
- protocol: TCP
port: 3000

Ingress Security

Implemented in Ingress Manifests:

metadata:
annotations:
# Rate limiting (future)
nginx.ingress.kubernetes.io/limit-rps: "100"
# IP whitelisting (for admin endpoints)
nginx.ingress.kubernetes.io/whitelist-source-range: "203.0.113.0/24,198.51.100.0/24"
# CORS configuration
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://algesta.com"
# Request body size limit
nginx.ingress.kubernetes.io/proxy-body-size: "10m"

MongoDB Network Security

IP Whitelist Configuration:

resource "mongodbatlas_project_ip_access_list" "allow_cidr" {
project_id = var.project_id
cidr_block = "0.0.0.0/0" # INSECURE! Restrict to AKS egress IPs
comment = "Allow all (for dev)"
}

Production Recommendation:

resource "mongodbatlas_project_ip_access_list" "aks_egress" {
project_id = var.project_id
cidr_block = "<AKS-EGRESS-IP>/32"
comment = "AKS Production Cluster"
}

Get AKS Egress IP:

Ventana de terminal
# Check AKS outbound IP
az aks show --resource-group rg-algesta-production --name aks-algesta-production \
--query networkProfile.loadBalancerProfile.effectiveOutboundIps -o table

Security Best Practices

Secrets Management

  • ✅ Use Azure Key Vault for centralized secret storage
  • ✅ Rotate secrets quarterly
  • ✅ Enable soft delete on Key Vault (7-day retention)
  • ❌ Never commit secrets to Git
  • ❌ Never log secret Valors

Container Security

  • ✅ Use slim base images (node:20-slim)
  • ✅ Run containers as non-root user
  • ✅ Scan images for vulnerabilities (Microsoft Defender, Snyk)
  • ✅ Pin base image versions
  • ❌ Don’t use latest tag in production

Network Security

  • ✅ Use TLS for all external traffic (cert-manager + Let’s Encrypt)
  • ✅ Restrict MongoDB IP whitelist to AKS egress IPs
  • ✅ Implement network policies (future)
  • ❌ Don’t expose sensitive Endpoints without authentication

Access Control

  • ✅ Use JWT for API authentication
  • ✅ Implement RBAC for admin Endpoints
  • ✅ Use Kubernetes RBAC for cluster access
  • ✅ Grant least privilege (read-only by default)
  • ❌ Don’t share credentials between environments

Compliance

  • ✅ Enable audit logging (Azure Monitor, Kubernetes audit logs)
  • ✅ Regularly review access permissions
  • ✅ Conduct security audits quarterly
  • ✅ Implement automated vulnerability scanning

Related Documentoation:

For Support:

  • Access Key Vault: Azure Portal → Key Vaults → akv-algesta-production
  • Check secrets: kubectl get secrets -n production
  • View certificates: kubectl get certificates -n production