Skip to content

Commit

Permalink
Enforce minimum TLS version for App Service #99 #100 (#101)
Browse files Browse the repository at this point in the history
  • Loading branch information
BernieWhite authored Jul 25, 2019
1 parent df50d14 commit d3d9000
Show file tree
Hide file tree
Showing 13 changed files with 760 additions and 27 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
## Unreleased

- Excluded global services from Azure.Resource.AllowedRegions. [#96](https://github.com/BernieWhite/PSRule.Rules.Azure/issues/96)
- Enforce minimum TLS version for App Service. [#99](https://github.com/BernieWhite/PSRule.Rules.Azure/issues/99)
- Updated App Service site rules to include slots. [#100](https://github.com/BernieWhite/PSRule.Rules.Azure/issues/100)
- `Azure.AppService.ARRAffinity` and `Azure.AppService.UseHTTPS` now run against slots.

## v0.3.0-B190710 (pre-release)

Expand Down
23 changes: 23 additions & 0 deletions docs/rules/en-US/Azure.AppService.MinTLS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
severity: Important
category: Security configuration
online version: https://github.com/BernieWhite/PSRule.Rules.Azure/blob/master/docs/rules/en-US/Azure.AppService.MinTLS.md
---

# Use minimum TLS version

## SYNOPSIS

App Service should reject TLS versions older then 1.2.

## DESCRIPTION

The minimum version of TLS that Azure App Service accepts is configurable. Older TLS versions are no longer considered secure by industry standards, such as PCI DSS.

App Service lets you disable outdated protocols and enforce TLS 1.2. By default use of a minimum of TLS 1.2 is enforced.

## RECOMMENDATION

Consider configuring the minimum supported TLS version to be 1.2.

For more information see [Enforce TLS versions](https://docs.microsoft.com/en-us/Azure/app-service/app-service-web-tutorial-custom-ssl#enforce-tls-versions) and [insecure protocols](https://docs.microsoft.com/en-us/Azure/app-service/overview-security#insecure-protocols-http-tls-10-ftp).
3 changes: 1 addition & 2 deletions docs/scenarios/install-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ The following modules are required for `PSRule.Rules.Azure` to work:
- Az.Resources
- Az.Security
- Az.Storage
- Az.Websites

The required version of each module will automatically be installed along-side `PSRule.Rules.Azure` when using `Install-Module` or `Update-Module` cmdlets.

Expand All @@ -38,7 +37,7 @@ Save for offline use from PowerShell Gallery:

```powershell
# Save module, in the .\modules directory
Save-Module -Name 'PSRule', 'PSRule.Rules.Azure', 'Az.Accounts', 'Az.Resources', 'Az.Security', 'Az.Storage', 'Az.Websites' -Path '.\modules';
Save-Module -Name 'PSRule', 'PSRule.Rules.Azure', 'Az.Accounts', 'Az.Resources', 'Az.Security', 'Az.Storage' -Path '.\modules';
```

> For pre-release versions the `-AllowPrerelease` switch must be added when calling `Install-Module` or `Save-Module`.
Expand Down
7 changes: 2 additions & 5 deletions pipeline.build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ task PSScriptAnalyzer NuGet, {

# Synopsis: Install PSRule
task PSRule NuGet, {
if ($Null -eq (Get-InstalledModule -Name PSRule -MinimumVersion 0.7.0 -ErrorAction Ignore)) {
Install-Module -Name PSRule -MinimumVersion 0.7.0 -AllowPrerelease -Scope CurrentUser -Force;
if ($Null -eq (Get-InstalledModule -Name PSRule -MinimumVersion 0.8.0-B190716 -AllowPrerelease -ErrorAction Ignore)) {
Install-Module -Name PSRule -MinimumVersion 0.8.0-B190716 -AllowPrerelease -Scope CurrentUser -Force;
}
Import-Module -Name PSRule -Verbose:$False;
}
Expand Down Expand Up @@ -180,9 +180,6 @@ task ModuleDependencies NuGet, PSRule, {
if ($Null -eq (Get-InstalledModule -Name Az.Storage -MinimumVersion 1.3.0 -ErrorAction Ignore)) {
Install-Module -Name Az.Storage -Scope CurrentUser -MinimumVersion 1.3.0 -Force -AllowClobber;
}
if ($Null -eq (Get-InstalledModule -Name Az.Websites -MinimumVersion 1.2.1 -ErrorAction Ignore)) {
Install-Module -Name Az.Websites -Scope CurrentUser -MinimumVersion 1.2.1 -Force;
}
}

task CopyModule {
Expand Down
4 changes: 0 additions & 4 deletions src/PSRule.Rules.Azure/PSRule.Rules.Azure.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ RequiredModules = @(
@{ ModuleName = 'Az.Resources'; ModuleVersion = '1.4.0' }
@{ ModuleName = 'Az.Security'; ModuleVersion = '0.7.4' }
@{ ModuleName = 'Az.Storage'; ModuleVersion = '1.3.0' }
@{ ModuleName = 'Az.Websites'; ModuleVersion = '1.2.1' }
)

# Assemblies that must be loaded prior to importing this module
Expand Down Expand Up @@ -97,9 +96,7 @@ AliasesToExport = @()

# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
PrivateData = @{

PSData = @{

# Tags applied to this module. These help with module discovery in online galleries.
Tags = @('Rule', 'PSRule', 'Azure')

Expand All @@ -114,7 +111,6 @@ PrivateData = @{

# ReleaseNotes of this module
ReleaseNotes = 'https://github.com/BernieWhite/PSRule.Rules.Azure/blob/master/CHANGELOG.md'

} # End of PSData hashtable

} # End of PrivateData hashtable
Expand Down
13 changes: 10 additions & 3 deletions src/PSRule.Rules.Azure/PSRule.Rules.Azure.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -364,9 +364,15 @@ function VisitWebApp {

process {
$resources = @();
$configResourceType = 'Microsoft.Web/sites/config';

$resources += Get-AzWebApp -ResourceGroupName $Resource.ResourceGroupName -Name $Resource.Name -DefaultProfile $Context;
$resource | Add-Member -MemberType NoteProperty -Name resources -Value $resources -PassThru;
# Handle slots
if ($Resource.ResourceType -eq 'Microsoft.Web/sites/slots') {
$configResourceType = 'Microsoft.Web/sites/slots/config';
}

$resources += Get-AzResource -Name $Resource.Name -ResourceType $configResourceType -ResourceGroupName $Resource.ResourceGroupName -DefaultProfile $Context -ApiVersion '2018-11-01' -ExpandProperties;
$Resource | Add-Member -MemberType NoteProperty -Name resources -Value $resources -PassThru;
}
}

Expand Down Expand Up @@ -445,7 +451,8 @@ function ExpandResource {
"Microsoft.DataFactory/factories" { VisitDataFactoryV2 @PSBoundParameters; }
# "Microsoft.Storage/storageAccounts" { VisitStorageAccount @PSBoundParameters; }
# "Microsoft.StorageSync/storageSyncServices" { VisitStorageSyncService @PSBoundParameters; }
# "Microsoft.Web/sites" { VisitWebApp @PSBoundParameters; }
"Microsoft.Web/sites" { VisitWebApp @PSBoundParameters; }
"Microsoft.Web/sites/slots" { VisitWebApp @PSBoundParameters; }
"Microsoft.RecoveryServices/vaults" { VisitRecoveryServices @PSBoundParameters; }
"Microsoft.Compute/virtualMachines" { VisitVirtualMachine @PSBoundParameters; }
"Microsoft.Subscription" { VisitSubscription @PSBoundParameters; }
Expand Down
5 changes: 5 additions & 0 deletions src/PSRule.Rules.Azure/en-AU/PSRule-rules.psd1
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@{
MinTLSVersion = "Minimum TLS version is set to {0}."
ResourceNotTagged = "The resource is not tagged."
AllowedRegionsNotConfigured = "The azureAllowedRegions option is not configured."
}
5 changes: 5 additions & 0 deletions src/PSRule.Rules.Azure/en-GB/PSRule-rules.psd1
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@{
MinTLSVersion = "Minimum TLS version is set to {0}."
ResourceNotTagged = "The resource is not tagged."
AllowedRegionsNotConfigured = "The azureAllowedRegions option is not configured."
}
5 changes: 5 additions & 0 deletions src/PSRule.Rules.Azure/en-US/PSRule-rules.psd1
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@{
MinTLSVersion = "Minimum TLS version is set to {0}."
ResourceNotTagged = "The resource is not tagged."
AllowedRegionsNotConfigured = "The azureAllowedRegions option is not configured."
}
13 changes: 11 additions & 2 deletions src/PSRule.Rules.Azure/rules/Azure.AppService.Rule.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,24 @@ Rule 'Azure.AppService.MinPlan' -If { ResourceType 'Microsoft.Web/serverfarms' }
}

# Synopsis: Disable client affinity for stateless services
Rule 'Azure.AppService.ARRAffinity' -If { ResourceType 'Microsoft.Web/sites' } -Tag @{ severity = 'Awareness'; category = 'Performance' } {
Rule 'Azure.AppService.ARRAffinity' -If { (ResourceType 'Microsoft.Web/sites') -or (ResourceType 'Microsoft.Web/sites/slots') } -Tag @{ severity = 'Awareness'; category = 'Performance' } {
Recommend 'Disable ARR affinity when not required'

$TargetObject.Properties.clientAffinityEnabled -eq $False
}

# Synopsis: Use HTTPS only
Rule 'Azure.AppService.UseHTTPS' -If { ResourceType 'Microsoft.Web/sites' } -Tag @{ severity = 'Important'; category = 'Security configuration' } {
Rule 'Azure.AppService.UseHTTPS' -If { (ResourceType 'Microsoft.Web/sites') -or (ResourceType 'Microsoft.Web/sites/slots') } -Tag @{ severity = 'Important'; category = 'Security configuration' } {
Recommend 'Disable HTTP when not required'

$TargetObject.Properties.httpsOnly -eq $True
}

# Synopsis: Use at least TLS 1.2
Rule 'Azure.AppService.MinTLS' -If { (ResourceType 'Microsoft.Web/sites') -or (ResourceType 'Microsoft.Web/sites/slots') } {
$siteConfig = @($TargetObject.resources | Where-Object -FilterScript {
($_.Type -eq 'Microsoft.Web/sites/config') -or
($_.Type -eq 'Microsoft.Web/sites/slots/config')
})
$siteConfig.Properties | Within 'minTlsVersion' '1.2' -Reason ($LocalizedData.MinTLSVersion -f $siteConfig.Properties.minTlsVersion)
}
1 change: 1 addition & 0 deletions src/PSRule.Rules.Azure/rules/Azure.Resource.Rule.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ if ($Null -eq $Configuration.azureAllowedRegions) {

# Synopsis: Resources should be tagged
Rule 'Azure.Resource.UseTags' -If { (SupportsTags) } -Tag @{ severity = 'Awareness'; category = 'Operations management' } {
Reason $LocalizedData.ResourceNotTagged
# List of resource that support tags can be found here: https://docs.microsoft.com/en-us/azure/azure-resource-manager/tag-support
(Exists 'Tags') -and
(($TargetObject.Tags.PSObject.Members | Where-Object { $_.MemberType -eq 'NoteProperty' }) -ne $Null)
Expand Down
32 changes: 24 additions & 8 deletions tests/PSRule.Rules.Azure.Tests/Azure.AppService.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ Describe 'Azure.AppService' {
# Fail
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' });
$ruleResult | Should -Not -BeNullOrEmpty;
$ruleResult.Length | Should -Be 1;
$ruleResult.TargetName | Should -Be 'site-B';
$ruleResult.Length | Should -Be 2;
$ruleResult.TargetName | Should -Be 'site-B', 'site-B/staging';

# Pass
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
$ruleResult | Should -Not -BeNullOrEmpty;
$ruleResult.Length | Should -Be 1;
$ruleResult.TargetName | Should -Be 'site-A';
$ruleResult.Length | Should -Be 2;
$ruleResult.TargetName | Should -Be 'site-A', 'site-A/staging';
}

It 'Azure.AppService.UseHTTPS' {
Expand All @@ -80,14 +80,30 @@ Describe 'Azure.AppService' {
# Fail
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' });
$ruleResult | Should -Not -BeNullOrEmpty;
$ruleResult.Length | Should -Be 1;
$ruleResult.TargetName | Should -Be 'site-B';
$ruleResult.Length | Should -Be 2;
$ruleResult.TargetName | Should -Be 'site-B', 'site-B/staging';

# Pass
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
$ruleResult | Should -Not -BeNullOrEmpty;
$ruleResult.Length | Should -Be 1;
$ruleResult.TargetName | Should -Be 'site-A';
$ruleResult.Length | Should -Be 2;
$ruleResult.TargetName | Should -Be 'site-A', 'site-A/staging';
}

It 'Azure.AppService.MinTLS' {
$filteredResult = $result | Where-Object { $_.RuleName -eq 'Azure.AppService.MinTLS' };

# Fail
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' });
$ruleResult | Should -Not -BeNullOrEmpty;
$ruleResult.Length | Should -Be 2;
$ruleResult.TargetName | Should -Be 'site-B', 'site-B/staging';

# Pass
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
$ruleResult | Should -Not -BeNullOrEmpty;
$ruleResult.Length | Should -Be 2;
$ruleResult.TargetName | Should -Be 'site-A', 'site-A/staging';
}
}
}
Loading

0 comments on commit d3d9000

Please sign in to comment.