From a05b3324de578eb84fe33ad29f90c38c7f6321f7 Mon Sep 17 00:00:00 2001 From: Facundo Gauna <fgauna12@gmail.com> Date: Mon, 19 Jul 2021 07:53:42 -0700 Subject: [PATCH 01/14] wip - adding new variables --- modules/disasterRecovery.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/modules/disasterRecovery.js b/modules/disasterRecovery.js index 58bbc24..81f1003 100644 --- a/modules/disasterRecovery.js +++ b/modules/disasterRecovery.js @@ -10,6 +10,8 @@ export function checkForAvailabilityZones(clusterDetails) { console.log(chalk.white("Checking for agent pools without multiple availability zones...")); + let details = [] + // Find agent pools with either no AZ's or a single AZ var agentPoolsWithNoAzs = clusterDetails .agentPoolProfiles @@ -25,7 +27,8 @@ export function checkForAvailabilityZones(clusterDetails) { return { checkId: 'DR-2', status: !agentPoolsWithNoAzs.length? ResultStatus.Pass: ResultStatus.Fail, - severity: Severity.High + severity: Severity.High, + details: details } } @@ -36,6 +39,8 @@ export function checkForVelero(pods) { console.log(chalk.white("Checking for Velero...")); + let details = [] + // Check if Velero is installed var veleroInstalled = pods .items @@ -51,7 +56,8 @@ export function checkForVelero(pods) { return { checkId: 'DR-5', status: veleroInstalled.length? ResultStatus.Pass: ResultStatus.Fail, - severity: Severity.Medium + severity: Severity.Medium, + details: details } } @@ -62,6 +68,8 @@ export function checkForControlPlaneSla(clusterDetails) { console.log(chalk.white("Checking for SLA for control plane...")); + let details = [] + // Check if SLA is configured for the management plane var slaConfigured = clusterDetails.sku.tier == "Paid"; @@ -75,6 +83,7 @@ export function checkForControlPlaneSla(clusterDetails) { return { checkId: 'DR-6', status: slaConfigured.length? ResultStatus.Pass: ResultStatus.Fail, - severity: Severity.High + severity: Severity.High, + details: details } } \ No newline at end of file From b5d91408f39e077e4c2eb0b8b8df2fc55cb83ec2 Mon Sep 17 00:00:00 2001 From: Facundo Gauna <fgauna12@gmail.com> Date: Mon, 19 Jul 2021 08:02:50 -0700 Subject: [PATCH 02/14] wip - reformatted DR-2 --- modules/disasterRecovery.js | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/modules/disasterRecovery.js b/modules/disasterRecovery.js index 81f1003..7164e74 100644 --- a/modules/disasterRecovery.js +++ b/modules/disasterRecovery.js @@ -2,6 +2,9 @@ import chalk from "chalk" import { equalsIgnoreCase } from '../helpers/stringCompare.js'; import { ResultStatus } from '../helpers/commandStatus.js'; import { Severity } from '../helpers/commandSeverity.js'; +import { EOL } from 'os'; + +const space = ' ' // // Checks if the cluster has agent pools without multiple availability zones @@ -19,9 +22,23 @@ export function checkForAvailabilityZones(clusterDetails) { // Log output if (agentPoolsWithNoAzs.length) { - console.log(chalk.red(`--- Found ${agentPoolsWithNoAzs.length} agent pools without multiple availability zones`)); + let message = `Found ${agentPoolsWithNoAzs.length} agent pools without multiple availability zones`; + + if (global.verbose) { + agentPoolsWithNoAzs.forEach(x => message += `${EOL}${space}${x.name}`); + } + + details.push({ + status: ResultStatus.Fail, + message: message + } + ); } else { - console.log(chalk.green("--- All agent pools have multiple availability zones")); + details.push({ + status: ResultStatus.Pass, + message: 'All agent pools have multiple availability zones' + } + ); } return { From 632af3c3f78a7e1972cf501692c9802ce16a4d65 Mon Sep 17 00:00:00 2001 From: Facundo Gauna <fgauna12@gmail.com> Date: Mon, 19 Jul 2021 11:39:35 -0700 Subject: [PATCH 03/14] wip - DR-5 formatting --- modules/disasterRecovery.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/disasterRecovery.js b/modules/disasterRecovery.js index 7164e74..80f48dd 100644 --- a/modules/disasterRecovery.js +++ b/modules/disasterRecovery.js @@ -65,9 +65,17 @@ export function checkForVelero(pods) { // Log output if (!veleroInstalled) { - console.log(chalk.red(`--- Velero is not installed`)); + details.push({ + status: ResultStatus.Fail, + message: "Velero is not installed" + } + ); } else { - console.log(chalk.green("--- Velero is installed")); + details.push({ + status: ResultStatus.Pass, + message: "Velero is installed" + } + ); } return { From 8d7e3d123d494d5c24004faef5c8dcf0ce21f180 Mon Sep 17 00:00:00 2001 From: Facundo Gauna <fgauna12@gmail.com> Date: Mon, 19 Jul 2021 11:41:56 -0700 Subject: [PATCH 04/14] wip - DR-6 formatting --- modules/disasterRecovery.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/disasterRecovery.js b/modules/disasterRecovery.js index 80f48dd..598c2c1 100644 --- a/modules/disasterRecovery.js +++ b/modules/disasterRecovery.js @@ -100,9 +100,17 @@ export function checkForControlPlaneSla(clusterDetails) { // Log output if (!slaConfigured) { - console.log(chalk.red(`--- An SLA has not been configured for the control plane`)); + details.push({ + status: ResultStatus.Fail, + message: "An SLA has not been configured for the control plane" + } + ); } else { - console.log(chalk.green("--- An SLA has been configured for the control plane")); + details.push({ + status: ResultStatus.Pass, + message: "An SLA has been configured for the control plane" + } + ); } return { From 686aed6a4d02e48b5d1511ec8715d3ca322ace20 Mon Sep 17 00:00:00 2001 From: Facundo Gauna <fgauna12@gmail.com> Date: Mon, 19 Jul 2021 11:43:12 -0700 Subject: [PATCH 05/14] wip - formatting --- modules/disasterRecovery.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/disasterRecovery.js b/modules/disasterRecovery.js index 598c2c1..2e84db9 100644 --- a/modules/disasterRecovery.js +++ b/modules/disasterRecovery.js @@ -13,7 +13,7 @@ export function checkForAvailabilityZones(clusterDetails) { console.log(chalk.white("Checking for agent pools without multiple availability zones...")); - let details = [] + let details = [] // Find agent pools with either no AZ's or a single AZ var agentPoolsWithNoAzs = clusterDetails @@ -43,7 +43,7 @@ export function checkForAvailabilityZones(clusterDetails) { return { checkId: 'DR-2', - status: !agentPoolsWithNoAzs.length? ResultStatus.Pass: ResultStatus.Fail, + status: !agentPoolsWithNoAzs.length ? ResultStatus.Pass : ResultStatus.Fail, severity: Severity.High, details: details } @@ -80,7 +80,7 @@ export function checkForVelero(pods) { return { checkId: 'DR-5', - status: veleroInstalled.length? ResultStatus.Pass: ResultStatus.Fail, + status: veleroInstalled.length ? ResultStatus.Pass : ResultStatus.Fail, severity: Severity.Medium, details: details } @@ -115,7 +115,7 @@ export function checkForControlPlaneSla(clusterDetails) { return { checkId: 'DR-6', - status: slaConfigured.length? ResultStatus.Pass: ResultStatus.Fail, + status: slaConfigured.length ? ResultStatus.Pass : ResultStatus.Fail, severity: Severity.High, details: details } From c694153f1ec5ba588dd27f0677753b0093fa5841 Mon Sep 17 00:00:00 2001 From: Facundo Gauna <fgauna12@gmail.com> Date: Mon, 19 Jul 2021 11:53:01 -0700 Subject: [PATCH 06/14] wip - img-3 formatting --- modules/imageManagement.js | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/modules/imageManagement.js b/modules/imageManagement.js index ca24b92..07545c2 100644 --- a/modules/imageManagement.js +++ b/modules/imageManagement.js @@ -3,7 +3,9 @@ import { equalsIgnoreCase } from '../helpers/stringCompare.js'; import { executeCommand } from '../helpers/commandHelpers.js'; import { ResultStatus } from '../helpers/commandStatus.js'; import { Severity } from '../helpers/commandSeverity.js'; +import { EOL } from 'os'; +const space = ' ' // // Checks for the 'only use allowed images' policy @@ -12,6 +14,8 @@ export function checkForAllowedImages(constraintTemplates) { console.log(chalk.white("Checking for 'Only use allowed images' policy...")); + let details = [] + // Check if allowed images constraint is defined var constraintDefined = constraintTemplates .items @@ -19,15 +23,24 @@ export function checkForAllowedImages(constraintTemplates) { // Log output if (!constraintDefined) { - console.log(chalk.red(`--- 'Only use allowed images' policy not applied`)); + details.push({ + status: ResultStatus.Fail, + message: "Only 'use allowed images' policy not applied" + } + ); } else { - console.log(chalk.green("--- 'Only use allowed images' policy applied")); + details.push({ + status: ResultStatus.Pass, + message: "'Only use allowed images' policy applied" + } + ); } return { checkId: 'IMG-3', status: constraintDefined.length? ResultStatus.Pass: ResultStatus.Fail, - severity: Severity.High + severity: Severity.High, + details: details } } @@ -102,7 +115,7 @@ export async function checkForAksAcrRbacIntegration(clusterDetails, containerReg if (!kubeletIdentityObjectId) { console.log(chalk.red('--- Could not determine cluster identity. Stopping check.')); return { - checkId: 'IMG-3', + checkId: 'IMG-5', status: ResultStatus.Fail, severity: Severity.High } From 4aa4be559721cd6283f720ef241348a97ea1b30d Mon Sep 17 00:00:00 2001 From: Facundo Gauna <fgauna12@gmail.com> Date: Mon, 19 Jul 2021 11:57:41 -0700 Subject: [PATCH 07/14] wip - img-4 formatting --- modules/imageManagement.js | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/modules/imageManagement.js b/modules/imageManagement.js index 07545c2..b57e511 100644 --- a/modules/imageManagement.js +++ b/modules/imageManagement.js @@ -51,6 +51,8 @@ export function checkForRuntimeContainerSecurity(pods) { console.log(chalk.white("Checking for runtime container security tools...")); + let details = [] + // Determine if Aqua (kube-enforcer) is installed var aquaInstalled = pods .items @@ -70,18 +72,30 @@ export function checkForRuntimeContainerSecurity(pods) { // Otherwise log the tool that was found var knownToolInstalled = aquaInstalled || anchoreInstalled || paloAltoInstalled; if (!knownToolInstalled) { - console.log(chalk.red(`--- A runtime container security tool was not found`)); + details.push({ + status: ResultStatus.Fail, + message: "A runtime container security tool was not found" + } + ); } else { - if (aquaInstalled) console.log(chalk.green(`--- Aqua Kube-Enforcer was found`)); - if (anchoreInstalled) console.log(chalk.green(`--- Anchore Engine was found`)); - if (paloAltoInstalled) console.log(chalk.green(`--- Palo Alto Twistlock was found`)); + let message = ""; + if (aquaInstalled) message = "Aqua Kube-Enforcer was found"; + if (anchoreInstalled) message = "Anchore Engine was found"; + if (paloAltoInstalled) message = "Palo Alto Twistlock was found"; + + details.push({ + status: ResultStatus.Pass, + message: "Velero is installed" + } + ); } return { checkId: 'IMG-4', status: knownToolInstalled? ResultStatus.Pass: ResultStatus.Fail, - severity: Severity.Medium + severity: Severity.Medium, + details: details } } From e8dda542e02a13f6aab0c1744860df9731d5dc3b Mon Sep 17 00:00:00 2001 From: Facundo Gauna <fgauna12@gmail.com> Date: Mon, 19 Jul 2021 12:02:47 -0700 Subject: [PATCH 08/14] wip - formatting img-5 --- modules/imageManagement.js | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/modules/imageManagement.js b/modules/imageManagement.js index b57e511..205d4d1 100644 --- a/modules/imageManagement.js +++ b/modules/imageManagement.js @@ -106,6 +106,8 @@ export async function checkForAksAcrRbacIntegration(clusterDetails, containerReg console.log(chalk.white("Checking for ACR/AKS RBAC integration for pulling images...")); + let details = [] + // Grab the kubelet identity from identity profile var identityProfile = clusterDetails.identityProfile; var kubeletIdentityObjectId = identityProfile && identityProfile.kubeletidentity.objectId; @@ -127,7 +129,11 @@ export async function checkForAksAcrRbacIntegration(clusterDetails, containerReg // Sanity check cluster identity if (!kubeletIdentityObjectId) { - console.log(chalk.red('--- Could not determine cluster identity. Stopping check.')); + details.push({ + status: ResultStatus.Pass, + message: "Could not determine cluster identity. Stopping check." + } + ); return { checkId: 'IMG-5', status: ResultStatus.Fail, @@ -149,15 +155,26 @@ export async function checkForAksAcrRbacIntegration(clusterDetails, containerReg // Log output if (problemRegistries.length) { - console.log(chalk.red(`--- ${problemRegistries.length} registries did not have AKS/ACR RBAC integration`)); + let message = `${problemRegistries.length} registries did not have AKS/ACR RBAC integration`; + + details.push({ + status: ResultStatus.Fail, + message: message + } + ); } else { - console.log(chalk.green("--- All registries have AKS/ACR RBAC integration")); + details.push({ + status: ResultStatus.Pass, + message: "All registries have AKS/ACR RBAC integration" + } + ); } return { checkId: 'IMG-5', status: !problemRegistries.length? ResultStatus.Pass: ResultStatus.Fail, - severity: Severity.High + severity: Severity.High, + details: details } } From 8d6d2c525e6548369f27ffb5fad40f4f30e1f04e Mon Sep 17 00:00:00 2001 From: Facundo Gauna <fgauna12@gmail.com> Date: Mon, 19 Jul 2021 12:12:11 -0700 Subject: [PATCH 09/14] wip - reformatted img-6 --- modules/imageManagement.js | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/modules/imageManagement.js b/modules/imageManagement.js index 205d4d1..b87035a 100644 --- a/modules/imageManagement.js +++ b/modules/imageManagement.js @@ -185,10 +185,15 @@ export function checkForPrivateEndpointsOnRegistries(containerRegistries) { console.log(chalk.white("Checking for private endpoints on container registries...")); + let details = [] + // If there are no container registries the test does not apply if (!containerRegistries.length) { - - console.log(chalk.gray("--- No registries were specified. Skipping check")); + details.push({ + status: ResultStatus.NotApply, + message: "No registries were specified. Skipping check" + } + ); return { checkId: 'IMG-6', @@ -203,15 +208,30 @@ export function checkForPrivateEndpointsOnRegistries(containerRegistries) { // Log output if (problemRegistries.length) { - console.log(chalk.red(`--- ${problemRegistries.length} registries did not have private endpoints configured`)); + let message = `${problemRegistries.length} registries did not have private endpoints configured`; + + if (global.verbose) { + problemRegistries.forEach(x => message += `${EOL}${space}${x.name}`); + } + + details.push({ + status: ResultStatus.Fail, + message: message + } + ); } else { - console.log(chalk.green("--- All registries have private endpoints configured")); + details.push({ + status: ResultStatus.Pass, + message: 'All registries have private endpoints configured' + } + ); } return { checkId: 'IMG-6', status: !problemRegistries.length? ResultStatus.Pass: ResultStatus.Fail, - severity: Severity.Medium + severity: Severity.Medium, + details: details } } From c5551c2ff9ff5f711319852fbb97b35afb4b56cf Mon Sep 17 00:00:00 2001 From: Facundo Gauna <fgauna12@gmail.com> Date: Mon, 19 Jul 2021 12:17:07 -0700 Subject: [PATCH 10/14] Documenting the image registry feature --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 1d114e1..e083e92 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,20 @@ $ aks-hc check all -g <resource group> -n <cluster name> -i ingress-nginx,kube-n $ exit ``` +### Optional - Azure Container Registry + +If you use Azure Container Registry (ACR), you can have this health check review some basic configuration. If will not inspect container images pushed to the registry. + +To do this, look at the container registries available then specify the `--image-registries` option. + +``` bash +$ az acr list --query "[].name" +foo1 +foo2 + +$ aks-hc check all -g <resource group> -n <cluster name> -i ingress-nginx,kube-node-lease,kube-public,kube-system --image-registries "foo1,foo2" +``` + ## Option B - Run with Azure Service Principal This option walks you through running the health check using an Azure Managed Identity so that it can be tied to a "service principal". Essentially, it avoids impersoning a user or running with someone's identity. From 7771707eb833708e25286b2c45366c2f67393c3f Mon Sep 17 00:00:00 2001 From: Facundo Gauna <fgauna12@gmail.com> Date: Mon, 19 Jul 2021 12:20:03 -0700 Subject: [PATCH 11/14] wip - formatting img-8 --- modules/imageManagement.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/modules/imageManagement.js b/modules/imageManagement.js index b87035a..cdbc4a0 100644 --- a/modules/imageManagement.js +++ b/modules/imageManagement.js @@ -242,6 +242,8 @@ export function checkForNoPrivilegedContainers(constraintTemplates) { console.log(chalk.white("Checking for 'No privileged containers' policy...")); + let details = [] + // Check if no privileged containers constraint is defined var constraintDefined = constraintTemplates .items @@ -249,14 +251,24 @@ export function checkForNoPrivilegedContainers(constraintTemplates) { // Log output if (!constraintDefined) { - console.log(chalk.red(`--- 'No privileged containers' policy not applied`)); + details.push({ + status: ResultStatus.Fail, + message: "'No privileged containers' policy not applied" + } + ); } else { console.log(chalk.green("--- 'No privileged containers' policy applied")); + details.push({ + status: ResultStatus.Pass, + message: "No privileged containers' policy applied" + } + ); } return { checkId: 'IMG-8', status: constraintDefined.length? ResultStatus.Pass: ResultStatus.Fail, - severity: Severity.High + severity: Severity.High, + details: details } } \ No newline at end of file From 9fb35f7370d84bd3a385f8306610aece458e11af Mon Sep 17 00:00:00 2001 From: Facundo Gauna <fgauna12@gmail.com> Date: Mon, 19 Jul 2021 12:20:20 -0700 Subject: [PATCH 12/14] wip -format file --- modules/imageManagement.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/imageManagement.js b/modules/imageManagement.js index cdbc4a0..fdd6927 100644 --- a/modules/imageManagement.js +++ b/modules/imageManagement.js @@ -38,7 +38,7 @@ export function checkForAllowedImages(constraintTemplates) { return { checkId: 'IMG-3', - status: constraintDefined.length? ResultStatus.Pass: ResultStatus.Fail, + status: constraintDefined.length ? ResultStatus.Pass : ResultStatus.Fail, severity: Severity.High, details: details } @@ -93,7 +93,7 @@ export function checkForRuntimeContainerSecurity(pods) { return { checkId: 'IMG-4', - status: knownToolInstalled? ResultStatus.Pass: ResultStatus.Fail, + status: knownToolInstalled ? ResultStatus.Pass : ResultStatus.Fail, severity: Severity.Medium, details: details } @@ -128,18 +128,18 @@ export async function checkForAksAcrRbacIntegration(clusterDetails, containerReg } // Sanity check cluster identity - if (!kubeletIdentityObjectId) { - details.push({ + if (!kubeletIdentityObjectId) { + details.push({ status: ResultStatus.Pass, message: "Could not determine cluster identity. Stopping check." } ); - return { + return { checkId: 'IMG-5', status: ResultStatus.Fail, severity: Severity.High } - } + } // Grab the roles for the identity var commandResults = await executeCommand(`az role assignment list --assignee '${kubeletIdentityObjectId}' --all`); @@ -150,7 +150,7 @@ export async function checkForAksAcrRbacIntegration(clusterDetails, containerReg containerRegistries.forEach(registry => { var regEx = new RegExp(`\/registries\/${registry.name}$`); if (!assignedRoles.some(x => x.roleDefinitionName == 'AcrPull' && regEx.test(x.scope))) - problemRegistries.push(registry.name); + problemRegistries.push(registry.name); }); // Log output @@ -172,7 +172,7 @@ export async function checkForAksAcrRbacIntegration(clusterDetails, containerReg return { checkId: 'IMG-5', - status: !problemRegistries.length? ResultStatus.Pass: ResultStatus.Fail, + status: !problemRegistries.length ? ResultStatus.Pass : ResultStatus.Fail, severity: Severity.High, details: details } @@ -229,7 +229,7 @@ export function checkForPrivateEndpointsOnRegistries(containerRegistries) { return { checkId: 'IMG-6', - status: !problemRegistries.length? ResultStatus.Pass: ResultStatus.Fail, + status: !problemRegistries.length ? ResultStatus.Pass : ResultStatus.Fail, severity: Severity.Medium, details: details } @@ -267,7 +267,7 @@ export function checkForNoPrivilegedContainers(constraintTemplates) { return { checkId: 'IMG-8', - status: constraintDefined.length? ResultStatus.Pass: ResultStatus.Fail, + status: constraintDefined.length ? ResultStatus.Pass : ResultStatus.Fail, severity: Severity.High, details: details } From 1d6cc6e7b11c11332f99dd8ac6606866b419c562 Mon Sep 17 00:00:00 2001 From: Facundo Gauna <fgauna12@gmail.com> Date: Mon, 19 Jul 2021 12:24:23 -0700 Subject: [PATCH 13/14] fixing typo --- modules/imageManagement.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imageManagement.js b/modules/imageManagement.js index fdd6927..54b2b48 100644 --- a/modules/imageManagement.js +++ b/modules/imageManagement.js @@ -86,7 +86,7 @@ export function checkForRuntimeContainerSecurity(pods) { details.push({ status: ResultStatus.Pass, - message: "Velero is installed" + message: message } ); } From b9822165f109d49cc7fd14817eff2583a93cec33 Mon Sep 17 00:00:00 2001 From: Facundo Gauna <fgauna12@gmail.com> Date: Mon, 19 Jul 2021 12:27:35 -0700 Subject: [PATCH 14/14] Adding verbose logging for img-5 --- modules/imageManagement.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/imageManagement.js b/modules/imageManagement.js index 54b2b48..59942cb 100644 --- a/modules/imageManagement.js +++ b/modules/imageManagement.js @@ -157,6 +157,10 @@ export async function checkForAksAcrRbacIntegration(clusterDetails, containerReg if (problemRegistries.length) { let message = `${problemRegistries.length} registries did not have AKS/ACR RBAC integration`; + if (global.verbose) { + problemRegistries.forEach(x => message += `${EOL}${space}${x}`); + } + details.push({ status: ResultStatus.Fail, message: message