Section 12

Reference Architectures

Production-ready reference architectures with security controls annotated at every layer. Use these as starting points for your own designs — adapt them to your specific threat model.

How to Use These Architectures

Each architecture includes a visual diagram, annotated security controls, common misconfigurations to avoid, and infrastructure-as-code snippets for critical components. Start with the one closest to your deployment model, then customize.

Architecture Selection Matrix

Choose your starting architecture based on team maturity, regulatory requirements, and deployment model. Most organizations evolve through these patterns as they grow.

Factor Three-Tier Hub-Spoke Zero Trust Service Mesh Secure CI/CD Hybrid
Team Size 2–10 10+ Any 10+ Any 20+
Cloud Maturity Beginner Intermediate Intermediate Advanced Any Advanced
Regulatory Need Low Medium Medium–High Low–Med Any High
Complexity Low Medium Medium High Low–Med High
Start Here If… First cloud app Multi-team org Remote-first / BYO device Microservices already built Any new pipeline On-prem + cloud migration

1. Secure Three-Tier Web Application

The classic architecture for web applications — presentation, application logic, and data layers with security controls at each tier and trust boundary.

Secure Three-Tier Architecture

graph LR User["👤 Client"] subgraph DMZ["DMZ / Public"] CDN["CDN / WAF"] --> AppGW["App Gateway"] end subgraph AppTier["Application Tier"] App1["App Svc 1"] & App2["App Svc 2"] end subgraph DataTier["Data Tier"] DB[("Azure SQL")] & Cache[("Redis Cache")] end User -->|"HTTPS"| CDN AppGW --> App1 & App2 App1 & App2 --> DB & Cache

Key Security Controls

Network Controls

  • • Security Groups: least-privilege port access
  • • NSG rules: subnet-level deny rules
  • • No direct internet access to app/data tier
  • • NSG Flow Logs enabled

Application Controls

  • • JWT validation at API gateway
  • • RBAC/ABAC at service level
  • • Input validation + output encoding
  • • Rate limiting per authenticated user

Terraform — VNet with Network Security Groups

secure-vnet.tf
hcl
# Secure VNet with isolated subnets per tier
resource "azurerm_virtual_network" "main" {
  name                = "secure-app-vnet"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
}

# Public subnet (Application Gateway only - no application code here)
resource "azurerm_subnet" "public" {
  name                 = "public-subnet"
  resource_group_name  = azurerm_resource_group.main.name
  virtual_network_name = azurerm_virtual_network.main.name
  address_prefixes     = ["10.0.1.0/24"]
}

# Private subnet for application tier
resource "azurerm_subnet" "app" {
  name                 = "app-subnet"
  resource_group_name  = azurerm_resource_group.main.name
  virtual_network_name = azurerm_virtual_network.main.name
  address_prefixes     = ["10.0.10.0/24"]
}

# Isolated subnet for database (no internet route)
resource "azurerm_subnet" "data" {
  name                 = "data-subnet"
  resource_group_name  = azurerm_resource_group.main.name
  virtual_network_name = azurerm_virtual_network.main.name
  address_prefixes     = ["10.0.20.0/24"]
}

# NSG: Application Gateway - only HTTPS from internet
resource "azurerm_network_security_group" "appgw" {
  name                = "appgw-nsg"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
}

resource "azurerm_network_security_rule" "appgw_https_in" {
  name                        = "allow-https-inbound"
  priority                    = 100
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "443"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.main.name
  network_security_group_name = azurerm_network_security_group.appgw.name
}

# NSG: App tier - only from Application Gateway subnet
resource "azurerm_network_security_group" "app" {
  name                = "app-nsg"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
}

resource "azurerm_network_security_rule" "app_from_appgw" {
  name                        = "allow-appgw-inbound"
  priority                    = 100
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "8080"
  source_address_prefix       = "10.0.1.0/24"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.main.name
  network_security_group_name = azurerm_network_security_group.app.name
}

# NSG: Database tier - only from App subnet on port 1433
resource "azurerm_network_security_group" "db" {
  name                = "db-nsg"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
}

resource "azurerm_network_security_rule" "db_from_app" {
  name                        = "allow-app-to-db"
  priority                    = 100
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "1433"
  source_address_prefix       = "10.0.10.0/24"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.main.name
  network_security_group_name = azurerm_network_security_group.db.name
}

resource "azurerm_network_security_rule" "db_deny_outbound" {
  name                        = "deny-all-outbound"
  priority                    = 4096
  direction                   = "Outbound"
  access                      = "Deny"
  protocol                    = "*"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.main.name
  network_security_group_name = azurerm_network_security_group.db.name
}
# Secure VNet with isolated subnets per tier
resource "azurerm_virtual_network" "main" {
  name                = "secure-app-vnet"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
}

# Public subnet (Application Gateway only - no application code here)
resource "azurerm_subnet" "public" {
  name                 = "public-subnet"
  resource_group_name  = azurerm_resource_group.main.name
  virtual_network_name = azurerm_virtual_network.main.name
  address_prefixes     = ["10.0.1.0/24"]
}

# Private subnet for application tier
resource "azurerm_subnet" "app" {
  name                 = "app-subnet"
  resource_group_name  = azurerm_resource_group.main.name
  virtual_network_name = azurerm_virtual_network.main.name
  address_prefixes     = ["10.0.10.0/24"]
}

# Isolated subnet for database (no internet route)
resource "azurerm_subnet" "data" {
  name                 = "data-subnet"
  resource_group_name  = azurerm_resource_group.main.name
  virtual_network_name = azurerm_virtual_network.main.name
  address_prefixes     = ["10.0.20.0/24"]
}

# NSG: Application Gateway - only HTTPS from internet
resource "azurerm_network_security_group" "appgw" {
  name                = "appgw-nsg"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
}

resource "azurerm_network_security_rule" "appgw_https_in" {
  name                        = "allow-https-inbound"
  priority                    = 100
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "443"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.main.name
  network_security_group_name = azurerm_network_security_group.appgw.name
}

# NSG: App tier - only from Application Gateway subnet
resource "azurerm_network_security_group" "app" {
  name                = "app-nsg"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
}

resource "azurerm_network_security_rule" "app_from_appgw" {
  name                        = "allow-appgw-inbound"
  priority                    = 100
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "8080"
  source_address_prefix       = "10.0.1.0/24"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.main.name
  network_security_group_name = azurerm_network_security_group.app.name
}

# NSG: Database tier - only from App subnet on port 1433
resource "azurerm_network_security_group" "db" {
  name                = "db-nsg"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
}

resource "azurerm_network_security_rule" "db_from_app" {
  name                        = "allow-app-to-db"
  priority                    = 100
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "1433"
  source_address_prefix       = "10.0.10.0/24"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.main.name
  network_security_group_name = azurerm_network_security_group.db.name
}

resource "azurerm_network_security_rule" "db_deny_outbound" {
  name                        = "deny-all-outbound"
  priority                    = 4096
  direction                   = "Outbound"
  access                      = "Deny"
  protocol                    = "*"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.main.name
  network_security_group_name = azurerm_network_security_group.db.name
}

Common Misconfigurations

Database subnet has route to internet gateway

Data tier should have NO internet access. Use Private Endpoints for Azure services.

Security groups use 0.0.0.0/0 for app tier ingress

App tier should only accept traffic from the load balancer security group.

Azure SQL instance publicly accessible

Disable public network access. Access DB only through app tier or Azure Bastion.

Monitoring & Observability

monitoring.tf
hcl
# NSG Flow Logs — capture all network traffic for forensics
resource "azurerm_network_watcher_flow_log" "nsg" {
  network_watcher_name = azurerm_network_watcher.main.name
  resource_group_name  = azurerm_resource_group.main.name
  name                 = "nsg-flow-log"

  network_security_group_id = azurerm_network_security_group.app.id
  storage_account_id        = azurerm_storage_account.logs.id
  enabled                   = true
  version                   = 2

  retention_policy {
    enabled = true
    days    = 90
  }

  traffic_analytics {
    enabled               = true
    workspace_id          = azurerm_log_analytics_workspace.main.workspace_id
    workspace_region      = azurerm_log_analytics_workspace.main.location
    workspace_resource_id = azurerm_log_analytics_workspace.main.id
    interval_in_minutes   = 10
  }
}

# Microsoft Defender for Cloud — automated threat detection
resource "azurerm_security_center_subscription_pricing" "vms" {
  tier          = "Standard"
  resource_type = "VirtualMachines"
}

resource "azurerm_security_center_subscription_pricing" "sql" {
  tier          = "Standard"
  resource_type = "SqlServers"
}

# Azure Monitor alert — alert on high 5xx error rate
resource "azurerm_monitor_metric_alert" "high_5xx" {
  name                = "high-5xx-rate"
  resource_group_name = azurerm_resource_group.main.name
  scopes              = [azurerm_application_gateway.main.id]
  severity            = 1
  frequency           = "PT5M"
  window_size         = "PT15M"

  criteria {
    metric_namespace = "Microsoft.Network/applicationGateways"
    metric_name      = "UnhealthyHostCount"
    aggregation      = "Average"
    operator         = "GreaterThan"
    threshold        = 0
  }

  action {
    action_group_id = azurerm_monitor_action_group.security_alerts.id
  }
}

# Azure Monitor alert — detect unusual sign-in activity
resource "azurerm_monitor_activity_log_alert" "unusual_api" {
  name                = "unusual-api-calls"
  resource_group_name = azurerm_resource_group.main.name
  scopes              = ["/subscriptions/${data.azurerm_subscription.current.subscription_id}"]

  criteria {
    category = "Administrative"
    level    = "Error"
  }

  action {
    action_group_id = azurerm_monitor_action_group.security_alerts.id
  }
}
# NSG Flow Logs — capture all network traffic for forensics
resource "azurerm_network_watcher_flow_log" "nsg" {
  network_watcher_name = azurerm_network_watcher.main.name
  resource_group_name  = azurerm_resource_group.main.name
  name                 = "nsg-flow-log"

  network_security_group_id = azurerm_network_security_group.app.id
  storage_account_id        = azurerm_storage_account.logs.id
  enabled                   = true
  version                   = 2

  retention_policy {
    enabled = true
    days    = 90
  }

  traffic_analytics {
    enabled               = true
    workspace_id          = azurerm_log_analytics_workspace.main.workspace_id
    workspace_region      = azurerm_log_analytics_workspace.main.location
    workspace_resource_id = azurerm_log_analytics_workspace.main.id
    interval_in_minutes   = 10
  }
}

# Microsoft Defender for Cloud — automated threat detection
resource "azurerm_security_center_subscription_pricing" "vms" {
  tier          = "Standard"
  resource_type = "VirtualMachines"
}

resource "azurerm_security_center_subscription_pricing" "sql" {
  tier          = "Standard"
  resource_type = "SqlServers"
}

# Azure Monitor alert — alert on high 5xx error rate
resource "azurerm_monitor_metric_alert" "high_5xx" {
  name                = "high-5xx-rate"
  resource_group_name = azurerm_resource_group.main.name
  scopes              = [azurerm_application_gateway.main.id]
  severity            = 1
  frequency           = "PT5M"
  window_size         = "PT15M"

  criteria {
    metric_namespace = "Microsoft.Network/applicationGateways"
    metric_name      = "UnhealthyHostCount"
    aggregation      = "Average"
    operator         = "GreaterThan"
    threshold        = 0
  }

  action {
    action_group_id = azurerm_monitor_action_group.security_alerts.id
  }
}

# Azure Monitor alert — detect unusual sign-in activity
resource "azurerm_monitor_activity_log_alert" "unusual_api" {
  name                = "unusual-api-calls"
  resource_group_name = azurerm_resource_group.main.name
  scopes              = ["/subscriptions/${data.azurerm_subscription.current.subscription_id}"]

  criteria {
    category = "Administrative"
    level    = "Error"
  }

  action {
    action_group_id = azurerm_monitor_action_group.security_alerts.id
  }
}

2. Secure Cloud Landing Zone (Hub-Spoke)

A multi-account / multi-subscription architecture for enterprise cloud deployments. Central shared services hub with isolated spoke environments for each workload.

Hub-Spoke Cloud Landing Zone

graph LR subgraph Mgmt["🔐 Management"] IAM["Entra ID"] & Audit["Audit Logs"] end subgraph Hub["Shared Hub"] VWAN["Virtual WAN"] --- FW["Azure Firewall"] & DNS["DNS / Logs"] end subgraph Prod["Prod Spoke"] ProdVNet["Workload VNet"] end subgraph Dev["Dev Spoke"] DevVNet["Workload VNet"] end Mgmt -->|"Azure Policy"| Hub & Prod & Dev VWAN <-->|"Peering"| ProdVNet & DevVNet

Landing Zone Security Controls

Subscription-Level Controls

  • • Azure Policy preventing disabling Defender/Activity Log
  • • Mandatory encryption on Blob Storage/Managed Disks/Azure SQL
  • • Deny public Blob containers at subscription level
  • • Require MFA for all Entra ID users

Network Controls

  • • All egress through centralized inspection
  • • DNS filtering via Azure DNS Private Resolver
  • • NSG Flow Logs to central SIEM
  • • No direct internet access in spokes

Azure Policy — Deny Dangerous Actions

deny-dangerous-actions-policy.json
json
{
  "properties": {
    "displayName": "Deny Dangerous Actions",
    "policyType": "Custom",
    "mode": "All",
    "parameters": {},
    "policyRule": {
      "if": {
        "anyOf": [
          {
            "allOf": [
              {
                "field": "type",
                "equals": "Microsoft.Security/securityContacts"
              },
              {
                "field": "Microsoft.Security/securityContacts/alertNotifications",
                "equals": "Off"
              }
            ]
          },
          {
            "allOf": [
              {
                "field": "type",
                "equals": "Microsoft.Storage/storageAccounts"
              },
              {
                "field": "Microsoft.Storage/storageAccounts/allowBlobPublicAccess",
                "equals": true
              }
            ]
          },
          {
            "allOf": [
              {
                "field": "type",
                "equals": "Microsoft.Storage/storageAccounts"
              },
              {
                "not": {
                  "field": "Microsoft.Storage/storageAccounts/encryption.services.blob.enabled",
                  "equals": true
                }
              }
            ]
          }
        ]
      },
      "then": {
        "effect": "Deny"
      }
    }
  }
}
{
  "properties": {
    "displayName": "Deny Dangerous Actions",
    "policyType": "Custom",
    "mode": "All",
    "parameters": {},
    "policyRule": {
      "if": {
        "anyOf": [
          {
            "allOf": [
              {
                "field": "type",
                "equals": "Microsoft.Security/securityContacts"
              },
              {
                "field": "Microsoft.Security/securityContacts/alertNotifications",
                "equals": "Off"
              }
            ]
          },
          {
            "allOf": [
              {
                "field": "type",
                "equals": "Microsoft.Storage/storageAccounts"
              },
              {
                "field": "Microsoft.Storage/storageAccounts/allowBlobPublicAccess",
                "equals": true
              }
            ]
          },
          {
            "allOf": [
              {
                "field": "type",
                "equals": "Microsoft.Storage/storageAccounts"
              },
              {
                "not": {
                  "field": "Microsoft.Storage/storageAccounts/encryption.services.blob.enabled",
                  "equals": true
                }
              }
            ]
          }
        ]
      },
      "then": {
        "effect": "Deny"
      }
    }
  }
}

3. Zero Trust Reference Architecture

Based on NIST SP 800-207. Every access request is authenticated, authorized, and encrypted — regardless of network location.

NIST 800-207 Zero Trust Architecture

graph LR Subject["👤 Subject"] --> PDP subgraph PDP["Policy Engine — PDP"] IdP["Identity Provider"] --- PAP["Policy Decision"] Threat["Threat Intel"] --- DataPolicy["Data Policy"] end PDP -->|"Allow / Deny"| PEP Subject --> PEP subgraph PEP["Enforcement Point — PEP"] CF["Cloudflare / Zscaler"] Proxy["API Gateway (mTLS)"] end PEP -->|"Encrypted"| Resources subgraph Resources["Enterprise Resources"] SaaS["SaaS"] & Legacy["On-prem"] & APIs["Cloud APIs"] & Lakes["Data Lakes"] end

Cloudflare Access — Zero Trust App Protection

cloudflare-access-policy.tf
hcl
# Cloudflare Access application (ZTNA - replaces VPN)
resource "cloudflare_access_application" "internal_app" {
  zone_id          = var.zone_id
  name             = "Internal Dashboard"
  domain           = "dashboard.internal.example.com"
  session_duration = "1h"

  # Require device posture check
  auto_redirect_to_identity = true
}

# Access policy: require corporate identity + device posture
resource "cloudflare_access_policy" "corp_policy" {
  application_id = cloudflare_access_application.internal_app.id
  zone_id        = var.zone_id
  name           = "Corporate Access"
  precedence     = 1
  decision       = "allow"

  include {
    login_method = ["oidc"]  # SSO via Okta/Entra
  }

  require {
    device_posture = [
      cloudflare_device_posture_rule.disk_encryption.id,
      cloudflare_device_posture_rule.os_version.id,
      cloudflare_device_posture_rule.firewall_enabled.id
    ]
  }
}

# Device posture: require disk encryption
resource "cloudflare_device_posture_rule" "disk_encryption" {
  account_id = var.account_id
  type       = "disk_encryption"
  name       = "Require disk encryption"
  input { require_all = true }
}
# Cloudflare Access application (ZTNA - replaces VPN)
resource "cloudflare_access_application" "internal_app" {
  zone_id          = var.zone_id
  name             = "Internal Dashboard"
  domain           = "dashboard.internal.example.com"
  session_duration = "1h"

  # Require device posture check
  auto_redirect_to_identity = true
}

# Access policy: require corporate identity + device posture
resource "cloudflare_access_policy" "corp_policy" {
  application_id = cloudflare_access_application.internal_app.id
  zone_id        = var.zone_id
  name           = "Corporate Access"
  precedence     = 1
  decision       = "allow"

  include {
    login_method = ["oidc"]  # SSO via Okta/Entra
  }

  require {
    device_posture = [
      cloudflare_device_posture_rule.disk_encryption.id,
      cloudflare_device_posture_rule.os_version.id,
      cloudflare_device_posture_rule.firewall_enabled.id
    ]
  }
}

# Device posture: require disk encryption
resource "cloudflare_device_posture_rule" "disk_encryption" {
  account_id = var.account_id
  type       = "disk_encryption"
  name       = "Require disk encryption"
  input { require_all = true }
}

4. Microservices with Service Mesh

Kubernetes-native architecture with Istio service mesh providing automatic mTLS, authorization policies, and observability for all service-to-service communication.

Microservices with Service Mesh (Istio)

graph LR Ingress["Ingress Gateway"] subgraph Mesh["☸ Service Mesh (mTLS everywhere)"] AppSvc["App Svc + Envoy"] OrderSvc["Order Svc + Envoy"] AuthSvc["Auth Svc + Envoy"] PaymentSvc["Payment Svc + Envoy"] end CP["istiod Control Plane"] Ingress -->|"mTLS"| AppSvc AppSvc <-->|"mTLS"| OrderSvc OrderSvc <-->|"mTLS"| PaymentSvc AppSvc <-->|"mTLS"| AuthSvc CP -.->|"Certs + Config"| Mesh

Istio Authorization Policy

istio-auth-policy.yaml
yaml
# Enforce mTLS for all traffic in the mesh
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: istio-system
spec:
  mtls:
    mode: STRICT   # All traffic must be mTLS

---
# Only allow order-svc to call payment-svc
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: payment-svc-policy
  namespace: production
spec:
  selector:
    matchLabels:
      app: payment-svc
  action: ALLOW
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/production/sa/order-svc"]
    to:
    - operation:
        methods: ["POST"]
        paths: ["/api/v1/charges", "/api/v1/refunds"]

---
# Deny all other traffic to payment-svc
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: payment-svc-deny-all
  namespace: production
spec:
  selector:
    matchLabels:
      app: payment-svc
  action: DENY
  rules: []  # Empty = deny everything not explicitly allowed
# Enforce mTLS for all traffic in the mesh
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: istio-system
spec:
  mtls:
    mode: STRICT   # All traffic must be mTLS

---
# Only allow order-svc to call payment-svc
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: payment-svc-policy
  namespace: production
spec:
  selector:
    matchLabels:
      app: payment-svc
  action: ALLOW
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/production/sa/order-svc"]
    to:
    - operation:
        methods: ["POST"]
        paths: ["/api/v1/charges", "/api/v1/refunds"]

---
# Deny all other traffic to payment-svc
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: payment-svc-deny-all
  namespace: production
spec:
  selector:
    matchLabels:
      app: payment-svc
  action: DENY
  rules: []  # Empty = deny everything not explicitly allowed

Kubernetes Network Policy

network-policy.yaml
yaml
# Default deny all ingress and egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

---
# Allow order-svc to reach payment-svc on port 8080 only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-order-to-payment
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: payment-svc
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: order-svc
    ports:
    - protocol: TCP
      port: 8080
# Default deny all ingress and egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

---
# Allow order-svc to reach payment-svc on port 8080 only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-order-to-payment
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: payment-svc
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: order-svc
    ports:
    - protocol: TCP
      port: 8080

5. Secure CI/CD Pipeline

Security gates at every stage of the pipeline — from commit to production. Every artifact is scanned, signed, and verified before deployment.

Secure CI/CD Pipeline — Security Gates

graph LR Dev["✍️ Write Code"] G1["🔒 GATE 1 Secrets Scan Security Lint"] G2["🔍 GATE 2 SAST · SCA License Check"] G3["🧪 GATE 3 DAST Scan Pen Test Review"] G4["📦 GATE 4 Image Sign · OPA SBOM Generate"] Prod["🚀 Production"] Dev --> G1 --> G2 --> G3 --> G4 --> Prod

GitHub Actions — Secure Pipeline

.github/workflows/secure-pipeline.yml
yaml
name: Secure CI/CD Pipeline
on: [push, pull_request]

permissions:
  contents: read
  security-events: write

jobs:
  gate-1-secrets:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
      with: { fetch-depth: 0 }
    - uses: gitleaks/gitleaks-action@v2
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  gate-2-sast:
    needs: gate-1-secrets
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Semgrep SAST
      uses: semgrep/semgrep-action@v1
      with:
        config: >-
          p/security-audit
          p/owasp-top-ten
          p/secrets
    - name: Dependency scan
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: fs
        severity: CRITICAL,HIGH
        exit-code: 1  # Fail on critical/high

  gate-3-container:
    needs: gate-2-sast
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Build image
      run: docker build -t app:${{ github.sha }} .
    - name: Scan container image
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: app:${{ github.sha }}
        severity: CRITICAL,HIGH
        exit-code: 1
    - name: Generate SBOM
      uses: anchore/sbom-action@v0
      with:
        image: app:${{ github.sha }}
        format: spdx-json
    - name: Sign image with Cosign
      uses: sigstore/cosign-installer@v3
    - run: cosign sign --yes app:${{ github.sha }}

  gate-4-deploy:
    needs: gate-3-container
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    environment: production  # Requires approval
    steps:
    - name: Verify image signature
      run: cosign verify app:${{ github.sha }}
    - name: Deploy to production
      run: kubectl set image deployment/app app=app:${{ github.sha }}
name: Secure CI/CD Pipeline
on: [push, pull_request]

permissions:
  contents: read
  security-events: write

jobs:
  gate-1-secrets:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
      with: { fetch-depth: 0 }
    - uses: gitleaks/gitleaks-action@v2
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  gate-2-sast:
    needs: gate-1-secrets
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Semgrep SAST
      uses: semgrep/semgrep-action@v1
      with:
        config: >-
          p/security-audit
          p/owasp-top-ten
          p/secrets
    - name: Dependency scan
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: fs
        severity: CRITICAL,HIGH
        exit-code: 1  # Fail on critical/high

  gate-3-container:
    needs: gate-2-sast
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Build image
      run: docker build -t app:${{ github.sha }} .
    - name: Scan container image
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: app:${{ github.sha }}
        severity: CRITICAL,HIGH
        exit-code: 1
    - name: Generate SBOM
      uses: anchore/sbom-action@v0
      with:
        image: app:${{ github.sha }}
        format: spdx-json
    - name: Sign image with Cosign
      uses: sigstore/cosign-installer@v3
    - run: cosign sign --yes app:${{ github.sha }}

  gate-4-deploy:
    needs: gate-3-container
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    environment: production  # Requires approval
    steps:
    - name: Verify image signature
      run: cosign verify app:${{ github.sha }}
    - name: Deploy to production
      run: kubectl set image deployment/app app=app:${{ github.sha }}

6. Hybrid / Multi-Cloud Security

Interconnecting on-premises, AWS, Azure, and GCP environments while maintaining consistent security policies and identity federation.

Hybrid / Multi-Cloud Architecture

graph LR subgraph OnPrem["🏢 On-Premises"] AD["AD / LDAP"] Legacy["Legacy Apps"] SIEM["SIEM (Splunk)"] end subgraph Cloud["☁️ Cloud (Azure)"] Entra["Entra ID"] CloudApps["Cloud-Native Apps"] Defender["Defender for Cloud Activity Log"] end AD <-->|"SAML / OIDC Federation"| Entra OnPrem <-->|"VPN / ExpressRoute (IPSec)"| Cloud Defender -->|"Logs"| SIEM Legacy ---|"SAML / OIDC"| CloudApps

Identity Federation

  • • Single IdP as source of truth (Entra ID / Okta)
  • • SAML/OIDC federation to all cloud providers
  • • No local cloud accounts — federated only
  • • Consistent MFA policy across all environments

Network Security

  • • Encrypted site-to-site VPN or ExpressRoute
  • • Consistent DNS security across environments
  • • Centralized egress inspection
  • • Uniform firewall rules via IaC

Data Sovereignty

  • • Data residency requirements per region
  • • Encryption keys in jurisdiction-appropriate KMS
  • • GDPR / data localization compliance
  • • Cross-border transfer agreements

Unified Observability

  • • Central SIEM aggregating all logs
  • • Consistent alert rules across providers
  • • CSPM tools covering all clouds
  • • Single pane of glass for incidents

Security Architecture Decision Record (ADR) Template

Document every security architecture decision so future teams understand the context. Use this template for each significant decision.

adr-template.md
markdown
# ADR-{number}: {Title}

## Status
Proposed | Accepted | Deprecated | Superseded by ADR-{n}

## Context
What is the problem or situation that requires a decision?
What are the security requirements and constraints?

## Decision Drivers
- Compliance requirement (e.g., SOC 2, PCI DSS)
- Threat model finding (reference TM-{n})
- Performance / scalability requirement
- Operational complexity budget

## Considered Options
1. **Option A** — Description, pros, cons
2. **Option B** — Description, pros, cons
3. **Option C** — Description, pros, cons

## Decision
We chose **Option B** because...

## Security Implications
- **Risk accepted**: What residual risk remains?
- **Controls required**: What must be implemented alongside?
- **Monitoring**: What should we watch for?

## Consequences
- Positive: What improves?
- Negative: What trade-offs are we making?
- Related decisions: Links to dependent ADRs
# ADR-{number}: {Title}

## Status
Proposed | Accepted | Deprecated | Superseded by ADR-{n}

## Context
What is the problem or situation that requires a decision?
What are the security requirements and constraints?

## Decision Drivers
- Compliance requirement (e.g., SOC 2, PCI DSS)
- Threat model finding (reference TM-{n})
- Performance / scalability requirement
- Operational complexity budget

## Considered Options
1. **Option A** — Description, pros, cons
2. **Option B** — Description, pros, cons
3. **Option C** — Description, pros, cons

## Decision
We chose **Option B** because...

## Security Implications
- **Risk accepted**: What residual risk remains?
- **Controls required**: What must be implemented alongside?
- **Monitoring**: What should we watch for?

## Consequences
- Positive: What improves?
- Negative: What trade-offs are we making?
- Related decisions: Links to dependent ADRs

Use These as Starting Points

No reference architecture is one-size-fits-all. Start with the pattern closest to your deployment model, run a threat model against it, and adapt the controls to your specific risk profile and compliance requirements.

Framework Alignment

NIST CSF 2.0: PR (Protect), DE (Detect) — controls mapped per architecture
NIST SP 800-207: Zero Trust reference architecture (pattern 3)
CIS Controls v8.1: 3 (Data Protection), 6 (Access Control), 12 (Network), 16 (App Security)
Azure Well-Architected: Security Pillar best practices
Related: Security Frameworks → | Case Studies →
🎯

Reference Architecture Labs

Deploy and validate these reference architectures hands-on. Each lab builds production-grade infrastructure with real security controls.

🔧
Deploy Secure Three-Tier on Azure Custom Lab medium
Apply the secure-vnet.tf Terraform module to create the VNet with isolated subnetsDeploy an Application Gateway with HTTPS only and WAF rulesConfigure NSG rules with least-privilege port access per tierEnable NSG Flow Logs, Defender for Cloud, and Azure Monitor alerts from monitoring.tfVerify: attempt direct DB access from internet (should fail)Run Checkov/tfsec against your Terraform and fix all CRITICAL findings
🔧
Build a Secure CI/CD Pipeline Custom Lab medium
Implement the GitHub Actions secure pipeline from this guideAdd Gitleaks for secrets scanning (Gate 1)Add Semgrep SAST and Trivy SCA scanning (Gate 2)Add container image scanning and SBOM generation (Gate 3)Implement Cosign image signing and verification (Gate 4)Intentionally introduce a vulnerability and verify the pipeline catches it
🔧
Service Mesh Security with Istio Custom Lab hard
Deploy a multi-service app on Kubernetes with IstioEnable STRICT mTLS mode across the meshCreate AuthorizationPolicies that allow only documented service-to-service pathsApply default-deny NetworkPolicies for defense-in-depthVerify: attempt unauthorized service calls (should get 403 RBAC denied)Monitor traffic with Kiali dashboard and Envoy access logs