rules/Azure.AKS.Rule.ps1
# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # # Validation rules for Azure Kubernetes Service (AKS) # # Synopsis: AKS control plane and nodes pools should use a current stable release. Rule 'Azure.AKS.Version' -Ref 'AZR-000015' -Type 'Microsoft.ContainerService/managedClusters', 'Microsoft.ContainerService/managedClusters/agentPools' -Tag @{ release = 'GA'; ruleSet = '2020_06'; 'Azure.WAF/pillar' = 'Security'; } -Labels @{ 'Azure.ASB.v3/control' = 'PV-7' } { $minVersion = $Configuration.GetValueOrDefault('Azure_AKSMinimumVersion', $Configuration.AZURE_AKS_CLUSTER_MINIMUM_VERSION); if ($PSRule.TargetType -eq 'Microsoft.ContainerService/managedClusters') { $upgradeChannel = $TargetObject.properties.autoUpgradeProfile.upgradeChannel $expectedUpgradeChannels = @('rapid', 'stable', 'node-image') if ($upgradeChannel -in $expectedUpgradeChannels -and !(IsExport)) { $Assert.Pass(); } else { $Assert.Version($TargetObject, 'Properties.kubernetesVersion', ">=$minVersion"); } } elseif ($PSRule.TargetType -eq 'Microsoft.ContainerService/managedClusters/agentPools') { if (!$Assert.HasField($TargetObject, 'Properties.orchestratorVersion').Result) { $Assert.Pass(); } else { $Assert.Version($TargetObject, 'Properties.orchestratorVersion', ">=$minVersion"); } } } # Synopsis: AKS agent pools should run the same Kubernetes version as the cluster Rule 'Azure.AKS.PoolVersion' -Ref 'AZR-000016' -Type 'Microsoft.ContainerService/managedClusters' -Tag @{ release = 'GA'; ruleSet = '2020_06' } { $clusterVersion = $TargetObject.Properties.kubernetesVersion; $agentPools = @(GetAgentPoolProfiles); if ($agentPools.Length -eq 0) { return $Assert.Pass(); } foreach ($agentPool in $agentPools) { $Assert.HasDefaultValue($agentPool, 'orchestratorVersion', $clusterVersion). Reason($LocalizedData.AKSNodePoolVersion, $agentPool.name, $agentPool.orchestratorVersion); } } # Synopsis: AKS node pools should use scale sets Rule 'Azure.AKS.PoolScaleSet' -Ref 'AZR-000017' -Type 'Microsoft.ContainerService/managedClusters', 'Microsoft.ContainerService/managedClusters/agentPools' -Tag @{ release = 'GA'; ruleSet = '2020_06' } { $agentPools = @(GetAgentPoolProfiles); if ($agentPools.Length -eq 0) { return $Assert.Pass(); } foreach ($agentPool in $agentPools) { $Assert.HasFieldValue($agentPool, 'type', 'VirtualMachineScaleSets'). Reason($LocalizedData.AKSNodePoolType, $agentPool.name); } } # Synopsis: AKS nodes should use a minimum number of pods Rule 'Azure.AKS.NodeMinPods' -Ref 'AZR-000018' -Type 'Microsoft.ContainerService/managedClusters', 'Microsoft.ContainerService/managedClusters/agentPools' -Tag @{ release = 'GA'; ruleSet = '2020_06' } { $agentPools = @(GetAgentPoolProfiles); if ($agentPools.Length -eq 0) { return $Assert.Pass(); } foreach ($agentPool in $agentPools) { $Assert.GreaterOrEqual($agentPool, 'maxPods', $Configuration.Azure_AKSNodeMinimumMaxPods); } } -Configure @{ Azure_AKSNodeMinimumMaxPods = 50 } # Synopsis: Use Autoscaling to ensure AKS cluster is running efficiently with the right number of nodes for the workloads present. Rule 'Azure.AKS.AutoScaling' -Ref 'AZR-000019' -Type 'Microsoft.ContainerService/managedClusters', 'Microsoft.ContainerService/managedClusters/agentPools' -Tag @{ release = 'GA'; ruleSet = '2021_09'; } { $agentPools = @(GetAgentPoolProfiles); if ($agentPools.Length -eq 0) { return $Assert.Pass(); } foreach ($agentPool in $agentPools) { # Autoscaling only available on virtual machine scale sets if ($Assert.HasFieldValue($agentPool, 'type', 'VirtualMachineScaleSets').Result) { $Assert.HasFieldValue($agentPool, 'enableAutoScaling', $True).Reason($LocalizedData.AKSAutoScaling, $agentPool.name); } else { $Assert.Pass() } } } # Synopsis: AKS clusters using Azure CNI should use large subnets to reduce IP exhaustion issues. Rule 'Azure.AKS.CNISubnetSize' -Ref 'AZR-000020' -If { IsExport } -With 'Azure.AKS.AzureCNI' -Tag @{ release = 'GA'; ruleSet = '2021_09'; } { $clusterSubnets = @(GetSubResources -ResourceType 'Microsoft.Network/virtualNetworks/subnets'); if ($clusterSubnets.Length -eq 0) { return $Assert.Pass(); } $configurationMinimumSubnetSize = $Configuration.AZURE_AKS_CNI_MINIMUM_CLUSTER_SUBNET_SIZE; foreach ($subnet in $clusterSubnets) { $subnetAddressPrefixSize = [int]$subnet.Properties.addressPrefix.Split('/')[-1]; $Assert.LessOrEqual($subnetAddressPrefixSize, '.', $configurationMinimumSubnetSize). Reason( $LocalizedData.AKSAzureCNI, $subnet.Name, $configurationMinimumSubnetSize ); } } -Configure @{ AZURE_AKS_CNI_MINIMUM_CLUSTER_SUBNET_SIZE = 23 } # Synopsis: AKS clusters deployed with virtual machine scale sets should use availability zones in supported regions for high availability. Rule 'Azure.AKS.AvailabilityZone' -Ref 'AZR-000021' -Type 'Microsoft.ContainerService/managedClusters' -Tag @{ release = 'GA'; ruleSet = '2021_09'; } { $agentPools = @(GetAgentPoolProfiles); if ($agentPools.Length -eq 0) { return $Assert.Pass(); } $virtualMachineScaleSetProvider = [PSRule.Rules.Azure.Runtime.Helper]::GetResourceType('Microsoft.Compute', 'virtualMachineScaleSets'); $configurationZoneMappings = $Configuration.AZURE_AKS_ADDITIONAL_REGION_AVAILABILITY_ZONE_LIST; $providerZoneMappings = $virtualMachineScaleSetProvider.ZoneMappings; $mergedAvailabilityZones = PrependConfigurationZoneWithProviderZone -ConfigurationZone $configurationZoneMappings -ProviderZone $providerZoneMappings; $availabilityZones = GetAvailabilityZone -Location $TargetObject.Location -Zone $mergedAvailabilityZones; if (-not $availabilityZones) { return $Assert.Pass(); } $joinedZoneString = $availabilityZones -join ', '; foreach ($agentPool in $agentPools) { # Availability zones only available on virtual machine scale sets if ($Assert.HasFieldValue($agentPool, 'type', 'VirtualMachineScaleSets').Result) { $Assert.HasFieldValue($agentPool, 'availabilityZones'). Reason($LocalizedData.AKSAvailabilityZone, $agentPool.name, $TargetObject.Location, $joinedZoneString); } else { $Assert.Pass(); } } } -Configure @{ AZURE_AKS_ADDITIONAL_REGION_AVAILABILITY_ZONE_LIST = @() } # Synopsis: AKS clusters should collect security-based audit logs to assess and monitor the compliance status of workloads. Rule 'Azure.AKS.AuditLogs' -Ref 'AZR-000022' -Type 'Microsoft.ContainerService/managedClusters' -Tag @{ release = 'GA'; ruleSet = '2021_09'; 'Azure.WAF/pillar' = 'Security'; } -Labels @{ 'Azure.ASB.v3/control' = 'LT-4' } { $diagnosticLogs = @(GetSubResources -ResourceType 'Microsoft.Insights/diagnosticSettings', 'Microsoft.ContainerService/managedClusters/providers/diagnosticSettings'); $Assert.Greater($diagnosticLogs, '.', 0).Reason($LocalizedData.DiagnosticSettingsNotConfigured, $TargetObject.name); foreach ($setting in $diagnosticLogs) { $kubeAuditEnabledLog = @($setting.Properties.logs | Where-Object { $_.category -in 'kube-audit', 'kube-audit-admin' -and $_.enabled }); $guardEnabledLog = @($setting.Properties.logs | Where-Object { $_.category -eq 'guard' -and $_.enabled }); $auditLogsEnabled = $Assert.Greater($kubeAuditEnabledLog, '.', 0).Result -and $Assert.Greater($guardEnabledLog, '.', 0).Result; $Assert.Create($auditLogsEnabled, $LocalizedData.AKSAuditLogs, $setting.name); } } # Synopsis: AKS clusters should collect platform diagnostic logs to monitor the state of workloads. Rule 'Azure.AKS.PlatformLogs' -Ref 'AZR-000023' -Type 'Microsoft.ContainerService/managedClusters' -Tag @{ release = 'GA'; ruleSet = '2021_09'; 'Azure.WAF/pillar' = 'Security'; } -Labels @{ 'Azure.ASB.v3/control' = 'LT-4' } { $configurationLogCategoriesList = $Configuration.GetStringValues('AZURE_AKS_ENABLED_PLATFORM_LOG_CATEGORIES_LIST'); if ($configurationLogCategoriesList.Length -eq 0) { return $Assert.Pass(); } $diagnosticLogs = @(GetSubResources -ResourceType 'Microsoft.Insights/diagnosticSettings', 'Microsoft.ContainerService/managedClusters/providers/diagnosticSettings'); $Assert.Greater($diagnosticLogs, '.', 0).Reason($LocalizedData.DiagnosticSettingsNotConfigured, $TargetObject.name); $availableLogCategories = @{ Logs = @( 'cluster-autoscaler', 'kube-apiserver', 'kube-controller-manager', 'kube-scheduler' ) Metrics = @( 'AllMetrics' ) } $configurationLogCategories = @($configurationLogCategoriesList | Where-Object { $_ -in $availableLogCategories.Logs }); $configurationMetricCategories = @($configurationLogCategoriesList | Where-Object { $_ -in $availableLogCategories.Metrics }); $logCategoriesNeeded = [System.Math]::Min( $configurationLogCategories.Length, $availableLogCategories.Logs.Length ); $metricCategoriesNeeded = [System.Math]::Min( $configurationMetricCategories.Length, $availableLogCategories.Metrics.Length ); $logCategoriesJoinedString = $configurationLogCategoriesList -join ', '; foreach ($setting in $diagnosticLogs) { $platformLogs = @($setting.Properties.logs | Where-Object { $_.enabled -and $_.category -in $configurationLogCategories -and $_.category -in $availableLogCategories.Logs }); $metricLogs = @($setting.Properties.metrics | Where-Object { $_.enabled -and $_.category -in $configurationMetricCategories -and $_.category -in $availableLogCategories.Metrics }); $platformLogsEnabled = $Assert.HasFieldValue($platformLogs, 'Length', $logCategoriesNeeded).Result -and $Assert.HasFieldValue($metricLogs, 'Length', $metricCategoriesNeeded).Result $Assert.Create( $platformLogsEnabled, $LocalizedData.AKSPlatformLogs, $setting.name, $logCategoriesJoinedString ); } } -Configure @{ AZURE_AKS_ENABLED_PLATFORM_LOG_CATEGORIES_LIST = @( 'cluster-autoscaler', 'kube-apiserver', 'kube-controller-manager', 'kube-scheduler', 'AllMetrics' ) } # Synopsis: AKS clusters should have Uptime SLA enabled to ensure availability of control plane components for production workloads. Rule 'Azure.AKS.UptimeSLA' -Ref 'AZR-000285' -Type 'Microsoft.ContainerService/managedClusters' -Tag @{ release = 'GA'; ruleSet = '2022_09'; } { $Assert.Contains($TargetObject, 'sku.tier', 'Paid'); } # Synopsis: AKS clusters should use ephemeral OS disks which can provide lower read/write latency, along with faster node scaling and cluster upgrades. Rule 'Azure.AKS.EphemeralOSDisk' -Ref 'AZR-000287' -Level Warning -Type 'Microsoft.ContainerService/managedClusters', 'Microsoft.ContainerService/managedClusters/agentPools' -Tag @{ release = 'GA'; ruleSet = '2022_09' } { $agentPools = @(GetAgentPoolProfiles); if ($agentPools.Length -eq 0) { return $Assert.Pass(); } foreach ($agentPool in $agentPools) { $Assert.HasDefaultValue($agentPool, 'osDiskType', 'Ephemeral'). ReasonIf($agentPool.osDiskType, $LocalizedData.AKSEphemeralOSDiskNotConfigured); } } #region Helper functions function global:GetAgentPoolProfiles { [CmdletBinding()] [OutputType([PSObject])] param () process { if ($PSRule.TargetType -eq 'Microsoft.ContainerService/managedClusters') { $TargetObject.Properties.agentPoolProfiles; @(GetSubResources -ResourceType 'Microsoft.ContainerService/managedClusters/agentPools' | ForEach-Object { [PSCustomObject]@{ name = $_.name type = $_.properties.type maxPods = $_.properties.maxPods orchestratorVersion = $_.properties.orchestratorVersion enableAutoScaling = $_.properties.enableAutoScaling availabilityZones = $_.properties.availabilityZones osDiskType = $_.properties.osDiskType } }); } elseif ($PSRule.TargetType -eq 'Microsoft.ContainerService/managedClusters/agentPools') { [PSCustomObject]@{ name = $TargetObject.name type = $TargetObject.properties.type maxPods = $TargetObject.properties.maxPods orchestratorVersion = $TargetObject.properties.orchestratorVersion enableAutoScaling = $TargetObject.properties.enableAutoScaling availabilityZones = $TargetObject.properties.availabilityZones osDiskType = $TargetObject.properties.osDiskType } } } } #endregion Helper functions # SIG # Begin signature block # MIInlQYJKoZIhvcNAQcCoIInhjCCJ4ICAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDmAYpn1J9hOij1 # Ytvn/IjmebI8Nqzyfin/T9mAm4Jvc6CCDXYwggX0MIID3KADAgECAhMzAAACy7d1 # OfsCcUI2AAAAAALLMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjIwNTEyMjA0NTU5WhcNMjMwNTExMjA0NTU5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQC3sN0WcdGpGXPZIb5iNfFB0xZ8rnJvYnxD6Uf2BHXglpbTEfoe+mO//oLWkRxA # wppditsSVOD0oglKbtnh9Wp2DARLcxbGaW4YanOWSB1LyLRpHnnQ5POlh2U5trg4 # 3gQjvlNZlQB3lL+zrPtbNvMA7E0Wkmo+Z6YFnsf7aek+KGzaGboAeFO4uKZjQXY5 # RmMzE70Bwaz7hvA05jDURdRKH0i/1yK96TDuP7JyRFLOvA3UXNWz00R9w7ppMDcN # lXtrmbPigv3xE9FfpfmJRtiOZQKd73K72Wujmj6/Su3+DBTpOq7NgdntW2lJfX3X # a6oe4F9Pk9xRhkwHsk7Ju9E/AgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUrg/nt/gj+BBLd1jZWYhok7v5/w4w # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzQ3MDUyODAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAJL5t6pVjIRlQ8j4dAFJ # ZnMke3rRHeQDOPFxswM47HRvgQa2E1jea2aYiMk1WmdqWnYw1bal4IzRlSVf4czf # zx2vjOIOiaGllW2ByHkfKApngOzJmAQ8F15xSHPRvNMmvpC3PFLvKMf3y5SyPJxh # 922TTq0q5epJv1SgZDWlUlHL/Ex1nX8kzBRhHvc6D6F5la+oAO4A3o/ZC05OOgm4 # EJxZP9MqUi5iid2dw4Jg/HvtDpCcLj1GLIhCDaebKegajCJlMhhxnDXrGFLJfX8j # 7k7LUvrZDsQniJZ3D66K+3SZTLhvwK7dMGVFuUUJUfDifrlCTjKG9mxsPDllfyck # 4zGnRZv8Jw9RgE1zAghnU14L0vVUNOzi/4bE7wIsiRyIcCcVoXRneBA3n/frLXvd # jDsbb2lpGu78+s1zbO5N0bhHWq4j5WMutrspBxEhqG2PSBjC5Ypi+jhtfu3+x76N # mBvsyKuxx9+Hm/ALnlzKxr4KyMR3/z4IRMzA1QyppNk65Ui+jB14g+w4vole33M1 # pVqVckrmSebUkmjnCshCiH12IFgHZF7gRwE4YZrJ7QjxZeoZqHaKsQLRMp653beB # fHfeva9zJPhBSdVcCW7x9q0c2HVPLJHX9YCUU714I+qtLpDGrdbZxD9mikPqL/To # /1lDZ0ch8FtePhME7houuoPcMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 # IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg # Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03 # a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr # rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg # OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy # 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9 # sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh # dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k # A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB # w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn # Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90 # lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w # ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o # ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa # BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG # AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV # HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG # AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl # AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb # C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l # hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6 # I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0 # wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560 # STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam # ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa # J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah # XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA # 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt # Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr # /Xmfwb1tbWrJUnMTDXpQzTGCGXUwghlxAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAALLt3U5+wJxQjYAAAAAAsswDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIPji4+4kyOvwAsZgGqUGBIWv # h+59bwwaM6n+Bugy3YX/MEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEAiDiXiQLXO/2HIQVX7Fo1laAGE9ZITa3uH5/F408IvgdUldKH2wtrmWqm # 56cZo/95Qa0npQofiLngb65BxD7PobWyucTpHB4+6h1FyywfTMfFSm4rzaN69g7/ # ERCP9n5K/c5Uu3NiEsRpbArj8ySvtBJw2ZqEW6wjSQ2qfvNnqM9lnOAEQwKzVALS # z1Sh0lwPh/M++yWo7Wn2Emc6ueHTGs/pl7fgLhZl/qtwZveLuECUOqe+riCHHw+p # ThU9stqsVQmViMLePBDRgpJ27vOiHiCRvBOUuztVxMpJY7Mr43N2PD7wf8TTlwiY # H8go2I2swF5/sVXLdcYHspHXHAlfaKGCFv8wghb7BgorBgEEAYI3AwMBMYIW6zCC # FucGCSqGSIb3DQEHAqCCFtgwghbUAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFQBgsq # hkiG9w0BCRABBKCCAT8EggE7MIIBNwIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCAqyGhrwGb1GwaydjLM6BGya272Irq6ZLo+ocmPz/cJkgIGY20l86Nf # GBIyMDIyMTIwODAxMDcwNS45N1owBIACAfSggdCkgc0wgcoxCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVy # aWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkU1QTYtRTI3 # Qy01OTJFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloIIR # VzCCBwwwggT0oAMCAQICEzMAAAG+9CCi7pbWINYAAQAAAb4wDQYJKoZIhvcNAQEL # BQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE # AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjIxMTA0MTkwMTIy # WhcNMjQwMjAyMTkwMTIyWjCByjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEm # MCQGA1UECxMdVGhhbGVzIFRTUyBFU046RTVBNi1FMjdDLTU5MkUxJTAjBgNVBAMT # HE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQClX/LbPsNrucy7S3KQtjyiWHtnTcSoU3PeIWUyn2A59WZk # AGaF4JzztG491DY/44dQmKoJABY241Kgj9DWLETD0ADrnuV0Pxnf8SS2mbEocdq8 # 6HBBIU9ylMYVVcjEoLCg7zbiCLIc8bzh1+F2LpZTt/sP7zkto8HR06w8coowaUL2 # nrou/3JDO8CFkYWYWGW6wLL96CvPolf84c5P2oLC6CGsvQg9/jtQt7WlBIQSKHLj # fwnBL6tlTgBXK9BzOUwLbpexO4M+ARAqXPH2u7sS81X32X8oJT1tsV/lKeQ3WahS # ApSrT01aUrHMsYS+GR7ZA0yimfzomHV+X89V683/GtlKlXbesziUHuWHtdKwI94W # yVNiiMo3aKg4LqncHLuQSa9kKHqsCw8qwBEkhJ3MpAIyr6aoO6I/qav8u+5YqKc/ # 7ZkaYr8LX+yS+VOO0h6G7nTKhc0OWHUI30HdAuCVBj5QIESomiD8HECfelZ1HTWj # /rpchpyBcj93TAbb/HQ61uMQYCRpx9CWbDRsNzTZ2FAWSL/VD1VvCHiQLtWACIkD # xsLnMQhhYc1TsL4d7r0Hj/Z1mlGOB3mkSkdsX05iIB/uzkydgScc3/mj9sY7RqMB # vtUjh/1q/rawLrG+EpMHlHiWHEQxYXTPi/sFDkIfIw2Qv6hOfMkuqctV1ee4zQID # AQABo4IBNjCCATIwHQYDVR0OBBYEFOsqIBahhEGg8a1vC9uGFfprb6KqMB8GA1Ud # IwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCGTmh0 # dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMFRp # bWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4wXAYI # KwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMv # TWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwGA1Ud # EwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZIhvcNAQELBQADggIB # AMeV+71zQiaF0GKzXKPnpsG85LIakL+tJK3L7UXj1N/p+YUR6rGHBNMdUc54hE13 # yBwMtLPR3v2ZcKqrzKerqAmDLa7gvLICvYubDMVW67GgZVHxi5SdG2+wMfkn66fJ # 7+cyTAeIL4bzaHe5Dx9waP7YfSco+ZSlS19Cu4xRe/MuNXk3JGMOIIvlz9/l5ybP # TV2emcK8TqQjP8VOmS855UmTbYjZqQVmE/PbgPo5PoqRO3AFGlIQcNioJDhxn7tJ # fHuPPN3tv7Sn28NuioLLtLBaAqkZAb7BVsqtObiEqRkPNx0ASBip6FfPvwbTSZgg # uINPJSKTBCmhntqb2kDoF1M9j6jW/oJHNyd4g6clhqcdbPRH4oRH9lEW0sLIEy8v # NIcSfSxHT7SQuSWdwqMZ0DVgDjbM5vrXVR4gbK1n1WE3CfjCzkYnqfo8mYw877I8 # SQ7LZ/w4GK6FqqWKmJaHMa23lSwLSB4bSxb2rBrhABbWxBYiuFKXbgw45XA2X8Cb # 39mq8tFavXHie6l5Hwbv4M3KfgxODbzIVlFTWS1K/IExRK83Yr30E7qnWBLH/C9K # xHjl0bfc8Mbl8qoc6APFy2MFTltfj14mqM0vtL9Sd0sXtLQ5Yv2Z2T+M9Uc/Yjpe # 03QrhWN1HC8iCveM2JvcZnIYmc5Gn9kxtjYO/WYpzHt1MIIHcTCCBVmgAwIBAgIT # MwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv # b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcN # MzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIw # DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O1YLT # /e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYj # DLWNE893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/Y # JlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxqD89d # 9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmPfrVU # j9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSWrAFK # u75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv231f # gLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C # 89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYctenIPDC # +hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQcxWv2 # XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17aj54W # cmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQABMCMG # CSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cV # XQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/ # BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2Nz # L1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcU # AgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8G # A1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeG # RWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jv # b0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUH # MAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2Vy # QXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9n # ATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x5MKP # +2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74py27Y # P0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1AoL8Z # thISEV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNh # cy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7G # dP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4J # vbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3rsjo # iV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcVv7TO # PqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ # 1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lwY1NN # je6CbaUFEMFxBmoQtB1VM1izoXBm8qGCAs4wggI3AgEBMIH4oYHQpIHNMIHKMQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNy # b3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVT # TjpFNUE2LUUyN0MtNTkyRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg # U2VydmljZaIjCgEBMAcGBSsOAwIaAxUAaK1aUve8+7wQ04B76Lb7jB9MwHuggYMw # gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUF # AAIFAOc7PFwwIhgPMjAyMjEyMDgwMDIyNTJaGA8yMDIyMTIwOTAwMjI1MlowdzA9 # BgorBgEEAYRZCgQBMS8wLTAKAgUA5zs8XAIBADAKAgEAAgIRBQIB/zAHAgEAAgIR # xzAKAgUA5zyN3AIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAow # CAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAINb7O9Luym6 # iKHnL9OOz9xwlzbHdUnMkK0+KlBAA5OYCNNY1QIBKh5EQqPmtlA+jyEqywMkGxFS # pSIrrA98bN1RuiIwBx4n4VWGT3W/WOLXxdgkb0QkOLUH8964R1I21vbSEU8/+qGl # ktm1hGH5IwGThP2Kwu3o6yG+O1zMKp7PMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UE # BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc # BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0 # IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAG+9CCi7pbWINYAAQAAAb4wDQYJYIZI # AWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG # 9w0BCQQxIgQgFeKRPDUI1L84ZkPlkjXl7MibQkqhHujTK61y3ofrvpgwgfoGCyqG # SIb3DQEJEAIvMYHqMIHnMIHkMIG9BCCU7oqvrfb87L1ltc+uEQ+J00CD8V5/srdJ # mD4PGOEMLzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n # dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y # YXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMz # AAABvvQgou6W1iDWAAEAAAG+MCIEIIUfFzDh8zF4VGVEazV6mNgHGMEqGBLwTeB6 # 5G8EiEjoMA0GCSqGSIb3DQEBCwUABIICAFi+3W1k9vLQ7QfpfXWsa8K0mqJtT/D9 # gqLxhtGgiBkYFoPAWPXagXkMdupsx4AP7+oJiZzUfz2D3EeC8hiLGAFqMDnYPQgT # T7ddXtVr2/03/lzIsBXrjQRS3A+lD1fMGo0lH5Bn6otdM8SDiHqk4dx4WMhBzZNY # /qHDbtnDx3qxyv3gW63y+LxpsBlYPGoJW2locOScNTaD23Ddjk0Vh6QGs92rVx9R # UPQb78c6jBrp2y7Plj/wgNhUqALzXj+Cfh2roMRp27yJnrCKE697NR0B1sjuhANS # Xv8W/3ISVn0GFLUEmBBO1NXbItvJgp48e3oJE/oPRwJa7hiZ+QgU4X8GehbMVtQN # Wt7uD9pHDHq6PPkXsUTTarryFbwlETss5VI7Iw/HeVS4i0EW6AzQJYMeFngDwMU0 # PHyh3Qrv3Ir65H1aPyiiXMCwSpwm4MZgocXXF6VtZsZtreXnqRXtJj3aLa5gQseR # ofH83K/y7HPg5bvycjbvZzGT/QregRcp5F5NIVXpj07omaspdArtFexxypivySw5 # FsvhT6uYAgdvO2hQHhsyJg/FBhJOrU896zNUzpZ4XWwuXzrlYl4PAVBVnmx8Zp/C # m0hY3/3dvoBM/+rCP3AuQ8X37unGWrZ3N8Vji5BoPG5GpuRMyuHb/zz/GrFVEjDC # fIj1jqRzVL6a # SIG # End signature block |