diff --git a/ql/lib/codeql/bicep/Frameworks.qll b/ql/lib/codeql/bicep/Frameworks.qll index 9f3b65b..040067b 100644 --- a/ql/lib/codeql/bicep/Frameworks.qll +++ b/ql/lib/codeql/bicep/Frameworks.qll @@ -2,7 +2,8 @@ import frameworks.Microsoft.Cache import frameworks.Microsoft.Compute import frameworks.Microsoft.Containers import frameworks.Microsoft.General +import frameworks.Microsoft.ManagedContainers import frameworks.Microsoft.Network import frameworks.Microsoft.Storage import frameworks.Microsoft.Databases -import frameworks.Microsoft.KeyVault \ No newline at end of file +import frameworks.Microsoft.KeyVault diff --git a/ql/lib/codeql/bicep/frameworks/Microsoft/ManagedContainers.qll b/ql/lib/codeql/bicep/frameworks/Microsoft/ManagedContainers.qll new file mode 100644 index 0000000..8ebc10d --- /dev/null +++ b/ql/lib/codeql/bicep/frameworks/Microsoft/ManagedContainers.qll @@ -0,0 +1,167 @@ +private import bicep +private import codeql.bicep.Concepts + +module ManagedContainers { + /** + * Represents a Microsoft.ContainerService/managedClusters resource (AKS) in a Bicep file. + * See: https://learn.microsoft.com/en-us/azure/templates/microsoft.containerservice/managedclusters + */ + class ManagedContainerResource extends Resource { + /** + * Constructs a ManagedContainerResource for Microsoft.ContainerService/managedClusters resources. + */ + ManagedContainerResource() { + this.getResourceType().regexpMatch("^Microsoft.ContainerService/managedClusters@.*") + } + + /** + * Returns the properties object for the AKS resource. + */ + ManagedContainerProperties::Properties getProperties() { + result = this.getProperty("properties") + } + + /** + * Returns the kubernetesVersion property. + */ + StringLiteral getKubernetesVersion() { result = this.getProperties().getKubernetesVersion() } + + /** + * Returns the dnsPrefix property. + */ + StringLiteral getDnsPrefix() { result = this.getProperties().getDnsPrefix() } + + /** + * Returns the agentPoolProfiles property. + */ + ManagedContainerProperties::AgentPoolProfiles getAgentPoolProfiles() { + result = this.getProperties().getAgentPoolProfiles() + } + + /** + * Returns the networkProfile property. + */ + Network::NetworkProfile getNetworkProfile() { + result = this.getProperties().getNetworkProfile() + } + + override string toString() { result = "ManagedContainerResource" } + } + + module ManagedContainerProperties { + /** + * Represents the properties object for a Kubernetes (AKS) resource. + */ + class Properties extends Object { + private ManagedContainerResource resource; + + Properties() { this = resource.getProperty("properties") } + + ManagedContainerResource getManagedContainerResource() { result = resource } + + StringLiteral getKubernetesVersion() { result = this.getProperty("kubernetesVersion") } + + StringLiteral getDnsPrefix() { result = this.getProperty("dnsPrefix") } + + AgentPoolProfiles getAgentPoolProfiles() { + result = this.getProperty("agentPoolProfiles").(Array).getElements() + } + + Network::NetworkProfile getNetworkProfile() { result = this.getProperty("networkProfile") } + + ApiServerAccessProfile getApiServerAccessProfile() { + result = this.getProperty("apiServerAccessProfile") + } + + AddonProfiles getAddonProfiles() { result = this.getProperty("addonProfiles") } + + Expr getIdentity() { result = this.getProperty("identity") } + + Expr getLinuxProfile() { result = this.getProperty("linuxProfile") } + + Expr getWindowsProfile() { result = this.getProperty("windowsProfile") } + + Expr getServicePrincipalProfile() { result = this.getProperty("servicePrincipalProfile") } + + Expr getAadProfile() { result = this.getProperty("aadProfile") } + + Expr getAutoScalerProfile() { result = this.getProperty("autoScalerProfile") } + + Expr getHttpProxyConfig() { result = this.getProperty("httpProxyConfig") } + + Expr getPodIdentityProfile() { result = this.getProperty("podIdentityProfile") } + + Expr getWorkloadAutoScalerProfile() { result = this.getProperty("workloadAutoScalerProfile") } + + Expr getStorageProfile() { result = this.getProperty("storageProfile") } + + Sku getSku() { result = this.getProperty("sku") } + + Tags getTags() { result = this.getProperty("tags") } + + string toString() { result = "ManagedContainerProperties" } + } + + class AgentPoolProfiles extends Object { + private Properties properties; + + AgentPoolProfiles() { + this = properties.getProperty("agentPoolProfiles").(Array).getElements() + } + + StringLiteral getName() { result = this.getProperty("name") } + + StringLiteral getVmSize() { result = this.getProperty("vmSize") } + + Expr getCount() { result = this.getProperty("count") } + + Expr getOsType() { result = this.getProperty("osType") } + + Expr getMode() { result = this.getProperty("mode") } + + string toString() { result = "AgentPoolProfiles" } + } + + class ApiServerAccessProfile extends Object { + private Properties properties; + + ApiServerAccessProfile() { this = properties.getProperty("apiServerAccessProfile") } + + StringLiteral getEnablePrivateCluster() { result = this.getProperty("enablePrivateCluster") } + + StringLiteral getPrivateDnsZone() { result = this.getProperty("privateDnsZone") } + + string toString() { result = "ApiServerAccessProfile" } + } + + class AddonProfiles extends Object { + private Properties properties; + + AddonProfiles() { this = properties.getProperty("addonProfiles") } + + AddonKubeDashboard getKubeDashboard() { result = this.getProperty("kubeDashboard") } + + string toString() { result = "AddonProfiles" } + } + + class AddonKubeDashboard extends Object { + private AddonProfiles profiles; + + AddonKubeDashboard() { this = profiles.getProperty("kubeDashboard") } + + Boolean getEnabled() { result = this.getProperty("enabled") } + + string toString() { result = "AddonKubeDashboard" } + } + + class AddonAzurePolicy extends Object { + private AddonProfiles profiles; + + AddonAzurePolicy() { this = profiles.getProperty("azurePolicy") } + + Boolean getEnabled() { result = this.getProperty("enabled") } + + string toString() { result = "AddonAzurePolicy" } + } + } +} diff --git a/ql/lib/codeql/bicep/frameworks/Microsoft/Network.qll b/ql/lib/codeql/bicep/frameworks/Microsoft/Network.qll index 85e2d58..9d2ba6c 100644 --- a/ql/lib/codeql/bicep/frameworks/Microsoft/Network.qll +++ b/ql/lib/codeql/bicep/frameworks/Microsoft/Network.qll @@ -321,4 +321,27 @@ module Network { } } } + + class NetworkProfile extends Object { + private Resource resource; + + NetworkProfile() { + exists(Object props | + props = resource.getProperty("properties") and + this = props.getProperty("networkProfile") + ) + } + + Resource getResource() { result = resource } + + StringLiteral getNetworkPlugin() { result = this.getProperty("networkPlugin") } + + string networkPlugin() { result = this.getNetworkPlugin().getValue() } + + StringLiteral getNetworkPolicy() { result = this.getProperty("networkPolicy") } + + string networkPolicy() { result = this.getNetworkPolicy().getValue() } + + string toString() { result = "NetworkProfile" } + } } diff --git a/ql/test/library-tests/frameworks/managed-containers/ManagedContainers.expected b/ql/test/library-tests/frameworks/managed-containers/ManagedContainers.expected new file mode 100644 index 0000000..f457430 --- /dev/null +++ b/ql/test/library-tests/frameworks/managed-containers/ManagedContainers.expected @@ -0,0 +1,5 @@ +mcr +| app.bicep:2:1:70:1 | ManagedContainerResource | +mcrAgents +| app.bicep:2:1:70:1 | ManagedContainerResource | app.bicep:9:7:18:7 | AgentPoolProfiles | +| app.bicep:2:1:70:1 | ManagedContainerResource | app.bicep:19:7:26:7 | AgentPoolProfiles | diff --git a/ql/test/library-tests/frameworks/managed-containers/ManagedContainers.ql b/ql/test/library-tests/frameworks/managed-containers/ManagedContainers.ql new file mode 100644 index 0000000..dc379a2 --- /dev/null +++ b/ql/test/library-tests/frameworks/managed-containers/ManagedContainers.ql @@ -0,0 +1,10 @@ +import bicep + +query predicate mcr(ManagedContainers::ManagedContainerResource mcr) { any() } + +query predicate mcrAgents( + ManagedContainers::ManagedContainerResource mcr, + ManagedContainers::ManagedContainerProperties::AgentPoolProfiles agents +) { + mcr.getAgentPoolProfiles() = agents +} diff --git a/ql/test/library-tests/frameworks/managed-containers/app.bicep b/ql/test/library-tests/frameworks/managed-containers/app.bicep new file mode 100644 index 0000000..4abfba8 --- /dev/null +++ b/ql/test/library-tests/frameworks/managed-containers/app.bicep @@ -0,0 +1,70 @@ +// Example Bicep file for an Azure Kubernetes Service (AKS) cluster with custom node pool and settings +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-01-01' = { + name: 'myAksCluster' + location: 'eastus' + properties: { + kubernetesVersion: '1.29.0' + dnsPrefix: 'myaksdns' + agentPoolProfiles: [ + { + name: 'nodepool1' + count: 3 + vmSize: 'Standard_DS2_v2' + osType: 'Linux' + mode: 'System' + enableAutoScaling: true + minCount: 1 + maxCount: 5 + } + { + name: 'nodepool2' + count: 2 + vmSize: 'Standard_DS3_v2' + osType: 'Linux' + mode: 'User' + enableAutoScaling: false + } + ] + networkProfile: { + networkPlugin: 'azure' + loadBalancerSku: 'standard' + networkPolicy: 'azure' + } + apiServerAccessProfile: { + enablePrivateCluster: false + authorizedIpRanges: [ + '203.0.113.0/24' + '198.51.100.0/24' + ] + } + addonProfiles: { + kubeDashboard: { + enabled: false + } + azurePolicy: { + enabled: true + } + } + identity: { + type: 'SystemAssigned' + } + linuxProfile: { + adminUsername: 'azureuser' + ssh: { + publicKeys: [ + { + keyData: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD...generatedkey... user@host' + } + ] + } + } + sku: { + name: 'Basic' + tier: 'Free' + } + tags: { + environment: 'dev' + owner: 'team-aks' + } + } +}