Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions aqua.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ packages:
- name: evilmartians/lefthook@v2.0.9
- name: bridgecrewio/checkov@3.2.495
- name: kubernetes-sigs/krew@v0.4.5
- name: Azure/kubelogin@v0.2.13
29 changes: 27 additions & 2 deletions terraform/cluster/azure-aks/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ provider "azurerm" {

data "azurerm_client_config" "current" {}

data "azurerm_subscription" "current" {}

data "azurerm_virtual_network" "vnet" {
name = "${var.vnet_module_name}-${var.context_id}"
resource_group_name = "${var.vnet_module_name}-${var.context_id}"
Expand Down Expand Up @@ -233,8 +235,12 @@ resource "azurerm_kubernetes_cluster" "main" {
role_based_access_control_enabled = var.role_based_access_control_enabled
automatic_upgrade_channel = var.automatic_upgrade_channel
sku_tier = var.sku_tier
# checkov:skip=CKV_AZURE_6: This feature is in preview, we are using a public cluster for testing
# api_server_authorized_ip_ranges = [0.0.0.0/0]

# checkov:skip=CKV_AZURE_6: We allow user to restrict IPs or default to open (null)
api_server_access_profile {
authorized_ip_ranges = var.authorized_ip_ranges
}

# checkov:skip=CKV_AZURE_115: We are using a public cluster for testing
# private clusters are encouraged for production
private_cluster_enabled = var.private_cluster_enabled
Expand All @@ -244,6 +250,11 @@ resource "azurerm_kubernetes_cluster" "main" {
# checkov:skip=CKV_AZURE_141: We are setting this to false to avoid the creation of an AD
local_account_disabled = var.local_account_disabled

azure_active_directory_role_based_access_control {
azure_rbac_enabled = true
admin_group_object_ids = var.admin_object_ids
}

key_vault_secrets_provider {
secret_rotation_enabled = true
}
Expand Down Expand Up @@ -380,3 +391,17 @@ resource "local_file" "kube_config" {
content = azurerm_kubernetes_cluster.main.kube_config_raw
filename = local.kubeconfig_path
}

# Automatically assign "Azure Kubernetes Service RBAC Cluster Admin" to the
# identity running Terraform (the deployer) and any additional admins provided.
# This ensures immediate access when local_account_disabled is set to true.
resource "azurerm_role_assignment" "aks_rbac_admin" {
for_each = toset(concat(
[data.azurerm_client_config.current.object_id],
var.admin_object_ids
))

scope = azurerm_kubernetes_cluster.main.id
role_definition_name = "Azure Kubernetes Service RBAC Cluster Admin"
principal_id = each.value
}
151 changes: 149 additions & 2 deletions terraform/cluster/azure-aks/test.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ mock_provider "azurerm" {
object_id = "22222222-2222-2222-2222-222222222222"
}
}
mock_data "azurerm_subscription" {
defaults = {
subscription_id = "12345678-1234-9876-4563-123456789012"
}
}
mock_data "azurerm_virtual_network" {
defaults = {
subnets = ["private-1-test", "private-2-test", "private-3-test", "public-1-test", "public-2-test", "isolated-1-test", "isolated-2-test"]
Expand Down Expand Up @@ -76,15 +81,40 @@ run "minimal_configuration" {
}

assert {
condition = azurerm_kubernetes_cluster.main.local_account_disabled == false
error_message = "Local accounts should be enabled by default"
condition = azurerm_kubernetes_cluster.main.local_account_disabled == true
error_message = "Local accounts should be disabled by default"
}

assert {
condition = azurerm_kubernetes_cluster.main.identity[0].type == "SystemAssigned"
error_message = "Cluster should use system-assigned identity by default"
}

assert {
condition = azurerm_kubernetes_cluster.main.azure_active_directory_role_based_access_control[0].azure_rbac_enabled == true
error_message = "Azure RBAC should be enabled by default"
}

assert {
condition = length(azurerm_kubernetes_cluster.main.azure_active_directory_role_based_access_control[0].admin_group_object_ids) == 0
error_message = "Admin group object IDs should be empty by default"
}

assert {
condition = azurerm_kubernetes_cluster.main.api_server_access_profile[0].authorized_ip_ranges == null
error_message = "Authorized IP ranges should be null by default (allowing all)"
}

assert {
condition = length(azurerm_role_assignment.aks_rbac_admin) == 1
error_message = "Role assignment should be created for the deployer identity by default"
}

assert {
condition = azurerm_role_assignment.aks_rbac_admin["22222222-2222-2222-2222-222222222222"].role_definition_name == "Azure Kubernetes Service RBAC Cluster Admin"
error_message = "Role assignment should use 'Azure Kubernetes Service RBAC Cluster Admin' role"
}

assert {
condition = azurerm_kubernetes_cluster.main.oidc_issuer_enabled == true
error_message = "OIDC issuer should be enabled by default"
Expand Down Expand Up @@ -145,6 +175,8 @@ run "full_configuration" {
private_cluster_enabled = false
azure_policy_enabled = true
local_account_disabled = false
authorized_ip_ranges = ["10.0.0.0/8"]
admin_object_ids = ["55555555-5555-5555-5555-555555555555"]
enable_volume_snapshots = true
}

Expand Down Expand Up @@ -247,6 +279,36 @@ run "full_configuration" {
condition = contains(azurerm_role_definition.aks_kubelet_vmss_disk_manager.permissions[0].actions, "Microsoft.Compute/snapshots/write")
error_message = "Snapshot write permissions should be included when enable_volume_snapshots is true"
}

assert {
condition = length(azurerm_kubernetes_cluster.main.api_server_access_profile[0].authorized_ip_ranges) == 1
error_message = "Authorized IP ranges should contain 1 entry"
}

assert {
condition = contains(azurerm_kubernetes_cluster.main.api_server_access_profile[0].authorized_ip_ranges, "10.0.0.0/8")
error_message = "Authorized IP ranges should include 10.0.0.0/8"
}

assert {
condition = azurerm_kubernetes_cluster.main.azure_active_directory_role_based_access_control[0].azure_rbac_enabled == true
error_message = "Azure RBAC should be enabled"
}

assert {
condition = length(azurerm_kubernetes_cluster.main.azure_active_directory_role_based_access_control[0].admin_group_object_ids) == 1
error_message = "Admin group object IDs should contain 1 entry"
}

assert {
condition = contains(azurerm_kubernetes_cluster.main.azure_active_directory_role_based_access_control[0].admin_group_object_ids, "55555555-5555-5555-5555-555555555555")
error_message = "Admin group object IDs should include the specified object ID"
}

assert {
condition = length(azurerm_role_assignment.aks_rbac_admin) == 2
error_message = "Role assignments should be created for deployer plus 1 admin object ID (2 total)"
}
}

# Tests the private cluster configuration, ensuring that enabling the private_cluster_enabled
Expand Down Expand Up @@ -306,6 +368,91 @@ run "network_configuration" {
}
}

# Tests the authorized IP ranges configuration, ensuring that setting authorized_ip_ranges
# results in the API server access profile being configured with the specified IP ranges.
run "authorized_ip_ranges" {
command = plan

variables {
context_id = "test"
name = "windsor-aks"
cluster_name = "test-cluster"
kubernetes_version = "1.32"
authorized_ip_ranges = ["10.0.0.0/8", "192.168.0.0/16"]
}

assert {
condition = length(azurerm_kubernetes_cluster.main.api_server_access_profile[0].authorized_ip_ranges) == 2
error_message = "Authorized IP ranges should contain 2 entries"
}

assert {
condition = contains(azurerm_kubernetes_cluster.main.api_server_access_profile[0].authorized_ip_ranges, "10.0.0.0/8")
error_message = "Authorized IP ranges should include 10.0.0.0/8"
}

assert {
condition = contains(azurerm_kubernetes_cluster.main.api_server_access_profile[0].authorized_ip_ranges, "192.168.0.0/16")
error_message = "Authorized IP ranges should include 192.168.0.0/16"
}
}

# Tests the Azure RBAC configuration with admin object IDs, ensuring that the
# azure_active_directory_role_based_access_control block is configured correctly and
# role assignments are created for all specified admin object IDs plus the deployer.
run "azure_rbac_with_admin_object_ids" {
command = plan

variables {
context_id = "test"
name = "windsor-aks"
cluster_name = "test-cluster"
kubernetes_version = "1.32"
local_account_disabled = true
admin_object_ids = ["33333333-3333-3333-3333-333333333333", "44444444-4444-4444-4444-444444444444"]
}

assert {
condition = azurerm_kubernetes_cluster.main.azure_active_directory_role_based_access_control[0].azure_rbac_enabled == true
error_message = "Azure RBAC should be enabled"
}

assert {
condition = length(azurerm_kubernetes_cluster.main.azure_active_directory_role_based_access_control[0].admin_group_object_ids) == 2
error_message = "Admin group object IDs should contain 2 entries"
}

assert {
condition = contains(azurerm_kubernetes_cluster.main.azure_active_directory_role_based_access_control[0].admin_group_object_ids, "33333333-3333-3333-3333-333333333333")
error_message = "Admin group object IDs should include the first specified object ID"
}

assert {
condition = contains(azurerm_kubernetes_cluster.main.azure_active_directory_role_based_access_control[0].admin_group_object_ids, "44444444-4444-4444-4444-444444444444")
error_message = "Admin group object IDs should include the second specified object ID"
}

assert {
condition = length(azurerm_role_assignment.aks_rbac_admin) == 3
error_message = "Role assignments should be created for deployer plus 2 admin object IDs (3 total)"
}

assert {
condition = azurerm_role_assignment.aks_rbac_admin["22222222-2222-2222-2222-222222222222"].role_definition_name == "Azure Kubernetes Service RBAC Cluster Admin"
error_message = "Role assignment for deployer should use 'Azure Kubernetes Service RBAC Cluster Admin' role"
}

assert {
condition = azurerm_role_assignment.aks_rbac_admin["33333333-3333-3333-3333-333333333333"].role_definition_name == "Azure Kubernetes Service RBAC Cluster Admin"
error_message = "Role assignment for first admin should use 'Azure Kubernetes Service RBAC Cluster Admin' role"
}

assert {
condition = azurerm_role_assignment.aks_rbac_admin["44444444-4444-4444-4444-444444444444"].role_definition_name == "Azure Kubernetes Service RBAC Cluster Admin"
error_message = "Role assignment for second admin should use 'Azure Kubernetes Service RBAC Cluster Admin' role"
}
}

run "multiple_invalid_inputs" {
command = plan
expect_failures = [
Expand Down
14 changes: 13 additions & 1 deletion terraform/cluster/azure-aks/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
# Variables
#-----------------------------------------------------------------------------------------------------------------------

variable "admin_object_ids" {
type = list(string)
description = "List of Azure AD Object IDs (User or Group) to assign 'Azure Kubernetes Service RBAC Cluster Admin' role. Required when local_account_disabled is true to ensure access."
default = []
}

variable "context_path" {
type = string
description = "The path to the context folder, where kubeconfig is stored"
Expand Down Expand Up @@ -184,7 +190,13 @@ variable "azure_policy_enabled" {
variable "local_account_disabled" {
type = bool
description = "Whether to disable local accounts for the AKS cluster"
default = false
default = true
}

variable "authorized_ip_ranges" {
type = set(string)
description = "Set of authorized IP ranges to allow access to the API server. If null, allows all (0.0.0.0/0)."
default = null
}

variable "public_network_access_enabled" {
Expand Down
Loading