Skip to content

Add vulnerability assessment cmdlets for IAAS in Az.Security #14607

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Apr 2, 2021
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

using Microsoft.Azure.Commands.ScenarioTest;
using Microsoft.Azure.ServiceManagement.Common.Models;
using Microsoft.WindowsAzure.Commands.ScenarioTest;
using Xunit;

namespace Microsoft.Azure.Commands.Security.Test.ScenarioTests
{
public class SqlVulnerabilityAssessmentTests
{
private readonly XunitTracingInterceptor _logger;

public SqlVulnerabilityAssessmentTests(Xunit.Abstractions.ITestOutputHelper output)
{
_logger = new XunitTracingInterceptor(output);
XunitTracingInterceptor.AddToContext(_logger);
TestExecutionHelpers.SetUpSessionAndProfile();
}

/*
* This test is live only because it cannot be recorded.
* New-AzMonitorLogAnalyticsSolution cmdlet depends on Az.MonitoringSolution
* which is a generated module and is not currently supported by the testing framework
*/
[Fact]
[Trait(Category.AcceptanceType, Category.LiveOnly)]
public void TestAzSecuritySqlVulnerabilityAssessment()
{
TestController.NewInstance.RunPowerShellTest(_logger, "Test-AzSecuritySqlVulnerabilityAssessment");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# ----------------------------------------------------------------------------------
#
# Copyright Microsoft Corporation
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ----------------------------------------------------------------------------------

<#
.SYNOPSIS
Tests end to end scenario for SQL vulnerability assessment on SQL VM.
This test is live only because it cannot be recorded.
New-AzMonitorLogAnalyticsSolution cmdlet depends on Az.MonitoringSolution
which is a generated module and is not currently supported by the testing framework
#>
function Test-AzSecuritySqlVulnerabilityAssessment
{
# Setup
$testPrefix = "pssqlva"
$testParams = Get-SecuritySqlVulnerabilityAssessmentTestEnvironmentParameters $testPrefix
$vmResourceId = "/subscriptions/" + $testParams.subscriptionId + "/resourceGroups/" + $testParams.rgName + "/providers/Microsoft.Compute/VirtualMachines/" + $testParams.sqlVmNamePrefix
Create-TestEnvironmentWithParams $testParams

$vaScanRecord = Get-AzSecuritySqlVulnerabilityAssessmentScanRecord -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database master -ScanId latest
$resultsOnMaster = Get-AzSecuritySqlVulnerabilityAssessmentScanResult -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database master
$resultsWithFindingsOnMaster = $resultsOnMaster | where { $_.Status -eq "Finding" }

Assert-True { $resultsOnMaster.Count -eq $vaScanRecord.TotalRulesCount }
Assert-True { $resultsWithFindingsOnMaster.Count -eq $vaScanRecord.TotalFailedRulesCount }

$finding = $resultsWithFindingsOnMaster | select -first 1
$ruleResult = Get-AzSecuritySqlVulnerabilityAssessmentScanResult -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database master -ScanId $vaScanRecord.Name -RuleId $finding.Name

Assert-True { $finding.Name -eq $ruleResult.Name }

# check add baseline with latest.
Add-AzSecuritySqlVulnerabilityAssessmentBaseline -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database master -RuleId $finding.Name

$baseline = Get-AzSecuritySqlVulnerabilityAssessmentBaseline -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database master -RuleId $finding.Name

Assert-NotNull $baseline

$baseline | Remove-AzSecuritySqlVulnerabilityAssessmentBaseline -Force

Assert-Throws { Get-AzSecuritySqlVulnerabilityAssessmentBaseline -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database master -RuleId $finding.Name }

# check Add baseline with result
Add-AzSecuritySqlVulnerabilityAssessmentBaseline -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database master -RuleId $finding.Name -Baseline $finding.QueryResults

$baseline = Get-AzSecuritySqlVulnerabilityAssessmentBaseline -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database master -RuleId $finding.Name
Assert-NotNull $baseline

$baseline | Remove-AzSecuritySqlVulnerabilityAssessmentBaseline -Force

### Check piping

$resultsOnMsdb = Get-AzSecuritySqlVulnerabilityAssessmentScanResult -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database msdb
$resultsWithFindingsOnMsdb = $resultsOnMsdb | where { $_.Status -eq "Finding" }
$rulesNamesOnMsdb = $resultsWithFindingsOnMsdb | select -ExpandProperty Name
$rulesNamesOnMaster = $resultsWithFindingsOnMaster | select -ExpandProperty Name

# get rules intersection between master and msdb
$ruleWithFindingsOnBothDbs = $rulesNamesOnMaster | ? {$rulesNamesOnMsdb -contains $_}

if ($ruleWithFindingsOnBothDbs.Count -gt 0)
{
# add baseline on master
Add-AzSecuritySqlVulnerabilityAssessmentBaseline -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database master -RuleId $ruleWithFindingsOnBothDbs[0]

# bypass it to msdb
Get-AzSecuritySqlVulnerabilityAssessmentBaseline -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database master -RuleId $ruleWithFindingsOnBothDbs[0] `
| Add-AzSecuritySqlVulnerabilityAssessmentBaseline -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database msdb

$baseline = Get-AzSecuritySqlVulnerabilityAssessmentBaseline -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database msdb -RuleId $ruleWithFindingsOnBothDbs[0]
Assert-NotNull $baseline
}

# Set all latest results as Baseline
Set-AzSecuritySqlVulnerabilityAssessmentBaseline -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database master -Force
$vaScanRecord = Get-AzSecuritySqlVulnerabilityAssessmentScanRecord -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database master -ScanId latest

Assert-True { $vaScanRecord.State -eq "Passed"}
Assert-True { $vaScanRecord.TotalFailedRulesCount -eq 0 }

Get-AzSecuritySqlVulnerabilityAssessmentBaseline -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database master | Remove-AzSecuritySqlVulnerabilityAssessmentBaseline -Force

$baselineSet = @{}
$resultsWithFindingsOnMaster | select -skip 3 | ForEach-Object { $baselineSet.Add($_.RuleId, $_.QueryResults)}

Set-AzSecuritySqlVulnerabilityAssessmentBaseline -ResourceId $vmResourceId -WorkspaceId $testParams.workspaceId -Server $testParams.sqlServerName -Database master -BaselineSet $baselineSet -Force

Delete-TestEnvironments ($testParams)
}

<#
.SYNOPSIS
Gets the values of the parameters used at the tests
#>
function Get-SecuritySqlVulnerabilityAssessmentTestEnvironmentParameters ($testPrefix)
{
$location = Get-Location "Microsoft.Compute" "virtualMachines" "East Us 2 Euap";
$sqlVmName = getAssetName ($testPrefix +'vm');

return @{ subscriptionId = (Get-AzContext).Subscription.Id;
rgName = getAssetName ($testPrefix);
sqlVmNamePrefix = $sqlVmName;
sqlVmDomain_prefix = 'domainvm';
sqlVmMaxLength = 15;
sqlVmUserName = 'testuser';
sqlVmPassword = Generate-RandomVmPassword;
sqlServerImage = 'MicrosoftSQLServer:SQL2017-WS2016:Enterprise:latest';
sqlServerVmSize = 'Standard_DS2_v2';
sqlServerName = "MSSQLSERVER"
operationalInsightsWorkspaceName = getAssetName ($testPrefix +"psWorkspace");
workspaceId = "";
location = location;
vmLocation = $location.Replace(' ', '');
vmDomainNameLabel = (getAssetName ($sqlVmName +'-')).ToLower();
}

}

<#
.SYNOPSIS
Creates the basic test environment needed to perform the sql vulnerability assessment tests - resource group, VM, workspace,... etc
#>
function Create-TestEnvironmentWithParams ($testParams)
{
# Create a new resource group.
New-AzResourceGroup -Name $testParams.rgName -Location $testParams.location

$passWord = ConvertTo-SecureString -String $testParams.sqlVmPassword -AsPlainText -Force
$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $testParams.sqlVmUserName, $passWord

# Create the sql virtual machine.
Write-Host "ResourceGroupName " + $testParams.rgName + " -Location " + $testParams.location " -Size "+ $testParams.sqlServerVmSize + " -Image " + $testParams.sqlServerImage + " -Credential " + $cred " -Name " + $testParams.sqlVmNamePrefix

New-AzVm -ResourceGroupName $testParams.rgName -Location $testParams.vmLocation -Image $testParams.sqlServerImage -Credential $cred -Name $testParams.sqlVmNamePrefix -DomainNameLabel $testParams.vmDomainNameLabel

# Create the log analytics worskspace
$workspace = New-AzOperationalInsightsWorkspace -Location $testParams.location -Name $testParams.operationalInsightsWorkspaceName -ResourceGroupName $testParams.rgName
New-AzMonitorLogAnalyticsSolution -Type SQLVulnerabilityAssessment -ResourceGroupName $testParams.rgName -Location $testParams.location -WorkspaceResourceId $workspace.ResourceId

# Install microsoft Monitoring agent on the VM
$workspaceKeys = Get-AzOperationalInsightsWorkspaceSharedKey -Name $testParams.operationalInsightsWorkspaceName -ResourceGroupName $testParams.rgName
$publicSettings = @{"workspaceId" = $workspace.CustomerId}
$protectedSettings = @{"workspaceKey" = $workspaceKeys.PrimarySharedKey}

$testParams.workspaceId = $workspace.CustomerId;

Set-AzVMExtension -ExtensionName "MicrosoftMonitoringAgent" `
-ResourceGroupName $testParams.rgName `
-VMName $testParams.sqlVmNamePrefix`
-Publisher "Microsoft.EnterpriseCloud.Monitoring" `
-ExtensionType "MicrosoftMonitoringAgent" `
-TypeHandlerVersion 1.0 `
-Settings $publicSettings `
-ProtectedSettings $protectedSettings `
-Location $testParams.vmLocation

# Update the registery and restart the Monitoring agent to force a scan.
Invoke-AzVMRunCommand -ResourceGroupName $testParams.rgName -Name $testParams.sqlVmNamePrefix -CommandId 'RunPowerShellScript' -ScriptPath 'SqlVulnerabilityAssessmentTestResources\SetUpVm.ps1'

Start-Sleep -Seconds 180
}

<#
.SYNOPSIS
Deletes the Test enviroment once the test passes.
#>
function Delete-TestEnvironments ($testParams)
{
New-AzResourceGroup -Name $testParams.rgName
}

<#
.SYNOPSIS
Generates a random password for vm that satisfies these conditions:
- At least 15 character long
- Contains at at least one number
- Contains at least one lower case letter
- Contains at least one upper case letter
#>
function Generate-RandomVmPassword()
{
$password = ("abcdefghijklmnopqrstuvwxyz".tochararray() | sort {Get-Random})[0..4] -join ''
$password += ("ABCDEFGHIJKLMNOPQRSTUVWXYZ".tochararray() | sort {Get-Random})[0..7] -join ''
$password += ("0123456789".tochararray() | sort {Get-Random})[0..4] -join ''

return ($password.tochararray() | sort {Get-Random}) -join ''
}
29 changes: 28 additions & 1 deletion src/Security/Security.Test/ScenarioTests/TestController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
// ----------------------------------------------------------------------------------

using Microsoft.Azure.Commands.Common.Authentication;
using Microsoft.Azure.Management.Compute;
using Microsoft.Azure.Management.OperationalInsights;
using Microsoft.Azure.Management.Internal.Network.Version2017_10_01;
using Microsoft.Azure.Management.Internal.Resources;
using Microsoft.Azure.Management.Security;
using Microsoft.Azure.Management.Storage.Version2017_10_01;
Expand Down Expand Up @@ -56,12 +59,18 @@ public void RunPowerShellTest(ServiceManagement.Common.Models.XunitTracingInterc
SetupManagementClients(context);

_helper.SetupEnvironment(AzureModule.AzureResourceManager);
var computePath = _helper.GetRMModulePath(@"AzureRM.Compute.psd1");
var networkPath = _helper.GetRMModulePath(@"AzureRM.Network.psd1");
var operationalInsightsPath = _helper.GetRMModulePath(@"AzureRM.OperationalInsights.psd1");

var callingClassName = callingClassType?.Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries).Last();
_helper.SetupModules(
AzureModule.AzureResourceManager,
_helper.RMProfileModule,
_helper.GetRMModulePath(@"AzureRM.Security.psd1"),
computePath,
networkPath,
operationalInsightsPath,
"ScenarioTests\\Common.ps1",
"ScenarioTests\\" + callingClassName + ".ps1",
"AzureRM.Storage.ps1",
Expand All @@ -76,7 +85,10 @@ protected void SetupManagementClients(MockContext context)
var resourcesClient = GetResourcesClient(context);
var securityCenterClient = GetSecurityCenterClient(context);
var storageClient = GetStorageManagementClient(context);
_helper.SetupManagementClients(securityCenterClient, resourcesClient, storageClient);
var computeClient = GetComputeManagementClient(context);
var networkClient = GetNetworkManagementClient(context);
var operationalInsightsClient = GetOperationalInsightsManagementClient(context);
_helper.SetupManagementClients(securityCenterClient, resourcesClient, storageClient, computeClient, networkClient, operationalInsightsClient);
}

private static SecurityCenterClient GetSecurityCenterClient(MockContext context)
Expand All @@ -91,5 +103,20 @@ private static StorageManagementClient GetStorageManagementClient(MockContext co
{
return context.GetServiceClient<StorageManagementClient>(TestEnvironmentFactory.GetTestEnvironment());
}

private static ComputeManagementClient GetComputeManagementClient(MockContext context)
{
return context.GetServiceClient<ComputeManagementClient>(TestEnvironmentFactory.GetTestEnvironment());
}

private static NetworkManagementClient GetNetworkManagementClient(MockContext context)
{
return context.GetServiceClient<NetworkManagementClient>(TestEnvironmentFactory.GetTestEnvironment());
}

private static OperationalInsightsManagementClient GetOperationalInsightsManagementClient(MockContext context)
{
return context.GetServiceClient<OperationalInsightsManagementClient>(TestEnvironmentFactory.GetTestEnvironment());
}
}
}
6 changes: 5 additions & 1 deletion src/Security/Security.Test/Security.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@
<None Include="SqlInformationProtectionPolicies\SensitivityLabelsSharingSameId.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="SqlVulnerabilityAssessmentTestResources\SetUpVM.ps1">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<PackageReference Include="Microsoft.Azure.Management.SecurityCenter" Version="2.2.0" />
<PackageReference Include="Microsoft.Azure.Management.Compute" Version="44.0.0" />
<PackageReference Include="Microsoft.Azure.Management.OperationalInsights" Version="0.21.0-preview" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
New-Item -ItemType Directory -Force -Path C:\\Users\\testuser\\Desktop\\Va_Logs\
New-Item -ItemType Directory -Force -Path C:\\Users\\testuser\\Desktop\\Setup_Logs\
New-Item -Path HKLM:\\Software\\Microsoft\\AzureOperationalInsights\
Set-ItemProperty -Path HKLM:\\Software\\Microsoft\\AzureOperationalInsights -Name SqlVulnerabilityAssessment_LogDirectoryPath -Value C:\\Users\\testuser\\Desktop\\Va_Logs\
Set-ItemProperty -Path HKLM:\\Software\\Microsoft\\AzureOperationalInsights -Name SqlVulnerabilityAssessment_BypassHashCheck -Value true\
Set-ItemProperty -Path HKLM:\\Software\\Microsoft\\AzureOperationalInsights -Name SqlVulnerabilityAssessment_TestMachine -Value true

Start-Sleep -Seconds 60
Restart-Service HealthService
6 changes: 6 additions & 0 deletions src/Security/Security/Az.Security.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ CmdletsToExport = 'Get-AzSecurityAlert', 'Set-AzSecurityAlert',
'Get-AzSecurityAdvancedThreatProtection',
'Enable-AzSecurityAdvancedThreatProtection',
'Disable-AzSecurityAdvancedThreatProtection',
'Get-AzSecuritySqlVulnerabilityAssessmentScanRecord',
'Get-AzSecuritySqlVulnerabilityAssessmentScanResult',
'Add-AzSecuritySqlVulnerabilityAssessmentBaseline',
'Remove-AzSecuritySqlVulnerabilityAssessmentBaseline',
'Get-AzSecuritySqlVulnerabilityAssessmentBaseline',
'Set-AzSecuritySqlVulnerabilityAssessmentBaseline',
'Get-AzIotSecuritySolution', 'Set-AzIotSecuritySolution',
'Remove-AzIotSecuritySolution', 'Update-AzIotSecuritySolution',
'New-AzIotSecuritySolutionRecommendationConfigurationObject',
Expand Down
Loading