Operaciones de Seguridad
Tabla de Contenidos
- Propósito
- Configuración de Azure Key Vault
- Gestión de Secrets de Kubernetes
- Gestión de Certificados TLS
- Autenticación y Autorización
- Seguridad de Contenedores e Imágenes
- Seguridad de Red
- 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 stringjwt-secret: JWT signing key for authenticationapi-keys: External service API keys (SendGrid, Twilio, etc.)tls-certificates: Manual TLS certificates (if not using cert-manager)
Access Control (RBAC):
# 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-productionAccessing Secrets:
# Via Azure CLIaz keyvault secret show --name mongodb-uri --vault-name akv-algesta-production --query value -o tsv
# Set secretaz keyvault secret set --name jwt-secret --vault-name akv-algesta-production --value "supersecurejwtkey12345"Future: AKS Integration with CSI Driver:
# Enable Azure Key Vault Provider for Secrets Store CSI Driveraz aks enable-addons --addons azure-keyvault-secrets-provider \ --resource-group rg-algesta-production \ --name aks-algesta-productionKubernetes Secrets Management
Current Approach: Manual kubectl create secret commands
Production Secrets:
# MongoDB credentialskubectl create secret generic mongodb-credentials \ --from-literal=uri="mongodb+srv://admin:SecurePassword@cluster.mongodb.net/algesta?retryWrites=true&w=majority" \ --namespace=production
# JWT secretkubectl create secret generic jwt-credentials \ --from-literal=secret="supersecurejwtkey12345" \ --namespace=production
# Verifykubectl get secrets -n productionBest 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):
# View secret value (base64 encoded)kubectl get secret mongodb-credentials -n production -o yaml
# Decode valuekubectl get secret mongodb-credentials -n production -o jsonpath='{.data.uri}' | base64 -dRotating Secrets:
# Update existing secretkubectl 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 secretkubectl rollout restart deployment -n productionTLS Certificate Management
Tool: cert-manager + Let’s Encrypt
ClusterIssuer Configuration (ops-algesta/resources-k8s/cert-manager/Issuer.yaml):
apiVersion: cert-manager.io/v1kind: ClusterIssuermetadata: name: letsencrypt-prod namespace: cert-managerspec: acme: server: https://acme-v02.api.letsencrypt.org/directory email: j.leon@tresastronautas.com privateKeySecretRef: name: letsencrypt-prod solvers: - http01: ingress: ingressClassName: nginxClusterIssuer for Web App Routing (ops-algesta/resources-k8s/cert-manager/Issuer-webapprouting.yaml):
apiVersion: cert-manager.io/v1kind: ClusterIssuermetadata: name: letsencrypt-prod-webapprouting namespace: cert-managerspec: 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.comNote: 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:
- Ingress created with
cert-manager.io/cluster-issuerannotation - cert-manager creates Certificate resource
- ACME challenge (HTTP-01) validates domain ownership
- Certificate issued and stored in Kubernetes Secret
- Auto-renewal 30 days before expiration
Checking Certificate Estado:
# List certificateskubectl get certificates -n production
# View certificate detailskubectl describe certificate algesta-api-prod-tls -n production
# Check expiration datekubectl get secret algesta-api-prod-tls -n production -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -enddateManual Certificate Renewal:
# Delete certificate to trigger re-issuancekubectl delete certificate algesta-api-prod-tls -n production
# cert-manager will automatically recreatekubectl get certificate algesta-api-prod-tls -n production -wTroubleshooting Certificate Issues:
# Check cert-manager logskubectl logs -n cert-manager -l app=cert-manager --tail=50
# Check certificate order statuskubectl get certificaterequests -n productionkubectl get orders -n productionkubectl get challenges -n productionAuthentication 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: v1kind: Secretmetadata: name: azure-devops-sa-token namespace: connect-devops annotations: kubernetes.io/service-account.name: azure-devops-satype: kubernetes.io/service-account-tokenRole and RoleBinding (example):
apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: deployment-manager namespace: productionrules: - apiGroups: ["apps"] resources: ["deployments"] verbs: ["get", "list", "update", "patch"]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: azure-devops-deployment-manager namespace: productionsubjects: - kind: ServiceAccount name: azure-devops-sa namespace: connect-devopsroleRef: kind: Role name: deployment-manager apiGroup: rbac.authorization.k8s.ioContainer and Image Security
Image Scanning (Azure Container Registry)
Enable Vulnerability Scanning:
# Enable Microsoft Defender for Cloud for ACRaz security pricing create \ --name ContainerRegistry \ --tier Standard
# Scan specific imageaz 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 patchesDockerfile Security Best Practices
Implemented in algesta-api-gateway-nestjs/Dockerfile:
# Multi-stage build (reduces attack surface)FROM node:20-slim AS builderWORKDIR /appCOPY package*.json ./RUN npm ciCOPY . .RUN npm run build
# Final stage: minimal imageFROM node:20-slimWORKDIR /app
# Run as non-root userRUN groupadd -r appuser && useradd -r -g appuser appuserUSER appuser
COPY --from=builder /app/dist ./distCOPY --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-slim→node:20.10-slim) - Scan images regularly (Microsoft Defender, Trivy, Snyk)
Dependency Scanning
npm audit (CI/CD integration):
# Run during build stagenpm audit --production
# Fix vulnerabilities automaticallynpm audit fixSnyk (recommended for production):
# Install Snyk CLInpm install -g snyk
# Authenticatesnyk auth
# Test for vulnerabilitiessnyk test
# Monitor projectsnyk monitorNetwork Security
Network Policies (Future Implementación)
Example: Restrict Inter-Namespace Communication:
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: deny-all-ingress namespace: productionspec: podSelector: {} policyTypes: - Ingress---apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: allow-ingress-from-monitoring namespace: productionspec: podSelector: matchLabels: app: api-gateway policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: name: monitoring ports: - protocol: TCP port: 3000Ingress 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:
# Check AKS outbound IPaz aks show --resource-group rg-algesta-production --name aks-algesta-production \ --query networkProfile.loadBalancerProfile.effectiveOutboundIps -o tableSecurity 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
latesttag 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:
- Infrastructure as Code: Key Vault provisioning
- Kubernetes Operaciones: Secrets and RBAC
- Runbooks: Secret rotation procedures
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