Skip to content

Security Model

Chris Panagiotidis edited this page Jun 13, 2026 · 3 revisions

Security Model

The security model is built around private-first access, explicit public exposure, layered network controls, CI policy gates, and guarded Terraform state.

Security control map

Important source files:

Security Principles

Principle Repository implementation
No broad public management by default RDP CIDR validation and Load Balancer RDP NAT disabled
Private data plane first Private Endpoints and Private DNS for Key Vault, Storage, SQL
Explicit public exposure App Gateway, Load Balancer, VPN, NAT, and jumpbox public IP are toggles
Central inspection Azure Firewall and route tables available when enabled
Least privilege Custom RBAC roles available through governance module
Policy as code OPA checks and Azure Policy module
Observable controls Diagnostics and alerts wired through management and post-deployment diagnostics
Secret minimization GitHub OIDC instead of long-lived Azure credentials JSON

Threat Model

Threat Lab control Validation evidence
Internet scanning of RDP Public RDP rejected by validation and NAT rules disabled by default Plan review and NSG/public IP review
Public PaaS data-plane exposure Private endpoints, Private DNS, SQL public access disabled by default Azure Portal private endpoint and network settings
Secret leakage in repository Gitleaks and no AZURE_CREDENTIALS client secret JSON CI secret-scan result
Overprivileged deployment identity OIDC and documented least-privilege guidance Entra app and role assignment review
Drift from imperative commands Terraform-managed App Gateway backend and stateful plan/apply Plan shows backend pool changes
Missing diagnostics Management and post-deployment diagnostic settings Diagnostic settings export
Cost-driven security bypass cheap-lab profile and explicit expensive toggles Profile and cost estimate review

Remote Management Controls

Public RDP is controlled by four variables:

Variable Default intent Risk if changed
enable_jumpbox_public_ip Disable direct public jumpbox access Exposes management host to internet
allowed_jumpbox_source_ips Empty or trusted CIDRs only 0.0.0.0/0 allows global RDP attempts
allow_public_rdp_from_internet false Break-glass override for unsafe RDP CIDR
enable_lb_rdp_nat_rules false Directly exposes workload VMs through public LB NAT

Source:

Recommended management access path:

  1. Prefer VPN or private management path.
  2. If public access is temporarily required, use a specific trusted CIDR.
  3. Do not use 0.0.0.0/0.
  4. Disable public access immediately after testing.

PaaS Security

Shared services are deployed from:

Hardening behavior:

Service Hardened behavior
Key Vault Public network access controlled; network ACL default deny when Private Endpoints are enabled
SQL Public network access disabled by default; "Allow Azure Services" disabled by default
Storage HTTPS required; blob public access disabled; network rules support private-first access
Private DNS Blob, Key Vault, and SQL zones can be linked to the hub and spokes
Private Endpoints Key Vault, Storage, and SQL private endpoints are available in the shared PE subnet

Private Endpoint Validation

Validate private-first behavior after apply:

az network private-endpoint list --query "[].{name:name, rg:resourceGroup, state:provisioningState}" -o table
az network private-dns zone list --query "[].{name:name, rg:resourceGroup}" -o table

Pass conditions:

Service Pass condition
Key Vault Private endpoint exists when enabled and public access posture matches profile
Storage HTTPS-only and anonymous public blob access disabled
SQL Public network access disabled unless there is an explicit lab exception
DNS Private DNS zones are linked to VNets that need to resolve private endpoints

Test private DNS from a VM or runner inside the peered network. Public internet clients should not be used as proof that private endpoints work.

Network Security Groups

NSGs are created through modules/networking/nsg.

NSG locations:

  • Hub management subnet
  • App Gateway subnet
  • Identity domain controller subnet
  • Management jumpbox subnet
  • Shared services app subnet
  • Workload web subnet
  • Workload app subnet
  • Workload data subnet
  • Optional AKS subnet
  • Optional on-prem simulation subnet

Validation and testing:

CI Security Gates

The pipeline enforces:

  • Terraform format check
  • Terraform validate
  • tfsec
  • Checkov
  • Gitleaks
  • OPA policy check
  • Module version analysis

Workflow source: .github/workflows/terraform.yml

Custom actions:

RBAC Review Examples

Use least privilege for real subscriptions. Common review patterns:

Persona Expected role direction
Platform network operator Network Contributor scoped to networking resource groups where possible
Security reviewer Reader plus Security Reader or policy-specific access
Monitoring operator Monitoring Reader or Monitoring Contributor depending on action scope
Backup operator Backup Contributor scoped to backup vault/resource group
CI/CD identity Minimum deployment roles required for selected modules and profile

Do not use broad Owner assignments as a permanent CI/CD solution. If temporary Owner is needed for bootstrap, remove it after role assignments and state backend are working.

Secrets And Sensitive Values

Never commit:

  • Tenant IDs if you treat them as sensitive
  • Subscription IDs if your organization classifies them
  • Client secrets
  • Passwords
  • Private keys
  • Admin usernames/passwords
  • SAS tokens
  • Backend access keys
  • Terraform state

Use GitHub secrets for:

  • AZURE_CLIENT_ID
  • AZURE_TENANT_ID
  • AZURE_SUBSCRIPTION_ID
  • TF_STATE_RG
  • TF_STATE_SA
  • Optional INFRACOST_API_KEY

See Remote State and Secrets.

Security Review Checklist

Before apply:

  • enable_jumpbox_public_ip = false unless there is a trusted CIDR requirement.
  • allowed_jumpbox_source_ips does not contain 0.0.0.0/0.
  • enable_lb_rdp_nat_rules = false.
  • SQL public network access is not enabled.
  • Key Vault public access is not opened without a reason.
  • Storage public blob access is disabled.
  • Private Endpoints and Private DNS are enabled for shared services.
  • GitHub Actions uses OIDC, not AZURE_CREDENTIALS.
  • Destroy environments require approval.

For operational procedures, incident response, and pull request review, see Security Operations.

Next page: Security Operations

Clone this wiki locally