Patch Managerベースラインの作成から定期レポート送信までをCloudFormationで作成してみた
いわさです。
AWSでは、Systems Manager Patch Managerを使うことで、EC2インスタンスへのセキュリティパッチの管理や更新の自動化を行うことが可能です。
ただし、パッチマネージャーの導入を検討する際には、ベースラインをどうするか、パッチ承認をどうするか、インストールのタイミングは、など慎重に検討しなければいけない要素が多いと思います。
そこで、まずはすべてのパッチを対象としたベースラインでスキャンだけしておき、スキャンした結果を週次など定期的にレポートする程度のスモールスタートな方法から始めることにしました。
その上で定期的にレポートを確認し、判断のうえオンデマンドでパッチ適用やベースラインの見直しを行っていきます。
定期スキャン
定期スキャンは、パッチベースラインを作成し、パッチグループとの紐づけを行います。
専用のタグをEC2に付与することでインスタンスをパッチグループ配下で管理することができるようになります。
そしてメンテナンスウィンドウを使って、定期的なスキャンをスケジューリングします。
ベースライン適用はスキャンのみか、スキャン&インストールを選択できます。
今回はスキャンのみなので承認日数は0日にしています。
もしインストールまで行う場合はより承認日数を考えた運用設計が必要ですね。
パッチマネージャーのスキャン状況および結果は次のようにマネジメントコンソール上でレポートとして表示されます。
パッチマネージャー内のレポート内容をS3へエクスポートしつつSNSでの通知を行うことが可能です。
次項ではそのあたりを設定します。
定期レポート
レポート機能は以下の記事でも紹介されています。割と最近のアップデートです。
レポートはスケジュール実行が可能です。
実体としては、EventBridgeでSSM Automationを定期実行しています。
レポートはS3バケットにCSVファイルが出力されます。
Index,Instance ID,Instance name,Instance IP,Platform name,Platform version,SSM Agent version,Patch baseline,Patch group,Compliance status,Compliance severity,Noncompliant Critical severity patch count,Noncompliant High severity patch count,Noncompliant Medium severity patch count,Noncompliant Low severity patch count,Noncompliant Informational severity patch count,Noncompliant Unspecified severity patch count 0,i-002bd55d99254ce3f,,172.31.2.45,Microsoft Windows Server 2019 Datacenter,10.0.17763,3.0.529.0,pb-0bbd976a5e8553e9e,iwapatch,NON_COMPLIANT,UNSPECIFIED,0,0,0,0,0,4
通知にはAmazonSNSを使用しており、Eメールで以下の通知内容が送信されます。
Patch Summary was successfully exported as a CSV file. The file,iwarepo.csv, is located in the following Amazon S3 bucket: iwasa-report-123456789012-patch-report-bucket
CloudFormationテンプレート
定期スキャンと定期レポートでテンプレートを分けています。
SecurityHubなどで管理する場合は定期スキャンのみ使うなどを想定しています。
AWSTemplateFormatVersion: "2010-09-09" Parameters: PatchGroupName: Type: String Description: patch group name PatchScanSchedule: Type: String Default: "cron(0 0 3 ? * * *)" Description: "patch scan schedule cron expression (JST)" PatchTargetOperatingSystem: Type: String Default: WINDOWS AllowedValues: - AMAZON_LINUX - AMAZON_LINUX_2 - CENTOS - DEBIAN - MACOS - ORACLE_LINUX - REDHAT_ENTERPRISE_LINUX - SUSE - UBUNTU - WINDOWS # Metadata: Description: "" Resources: SSMPatchBaseline: Type: "AWS::SSM::PatchBaseline" Properties: Name: !Sub ${AWS::StackName}-fullscan-patchbaseline OperatingSystem: !Ref PatchTargetOperatingSystem ApprovalRules: PatchRules: - ApproveAfterDays: 0 PatchFilterGroup: PatchFilters: - Key: PATCH_SET Values: - OS - Key: PRODUCT Values: - "*" - Key: CLASSIFICATION Values: - "*" - Key: MSRC_SEVERITY Values: - "*" ComplianceLevel: UNSPECIFIED EnableNonSecurity: false ApprovedPatchesComplianceLevel: "UNSPECIFIED" ApprovedPatchesEnableNonSecurity: false RejectedPatchesAction: "ALLOW_AS_DEPENDENCY" PatchGroups: - !Ref PatchGroupName SSMMaintenanceWindow: Type: "AWS::SSM::MaintenanceWindow" Properties: Name: !Sub ${AWS::StackName}-maintenancewindow Schedule: !Ref PatchScanSchedule ScheduleTimezone: "Asia/Tokyo" Duration: 1 Cutoff: 0 AllowUnassociatedTargets: true SSMMaintenanceWindowTask: Type: "AWS::SSM::MaintenanceWindowTask" Properties: Name: !Sub ${AWS::StackName}-maintenancewindow-task WindowId: !Ref SSMMaintenanceWindow Targets: - Key: "WindowTargetIds" Values: - !Ref SSMMaintenanceWindowTarget TaskArn: "AWS-RunPatchBaseline" ServiceRoleArn: !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-service-role/ssm.amazonaws.com/AWSServiceRoleForAmazonSSM" TaskType: "RUN_COMMAND" TaskParameters: {} Priority: 1 MaxConcurrency: "50" MaxErrors: "0" TaskInvocationParameters: MaintenanceWindowRunCommandParameters: Parameters: Operation: - "Scan" SnapshotId: - "{{WINDOW_EXECUTION_ID}}" TimeoutSeconds: 600 SSMMaintenanceWindowTarget: Type: "AWS::SSM::MaintenanceWindowTarget" Properties: Name: !Sub ${AWS::StackName}-patch-target WindowId: !Ref SSMMaintenanceWindow ResourceType: "INSTANCE" Targets: - Key: "tag:Patch Group" Values: - !Ref PatchGroupName
AWSTemplateFormatVersion: "2010-09-09" Parameters: EMailAddress: Type: String Description: Specifies your E-Mail for Patch Scan Report. ScheduleExpression: Type: String Default: "cron(10 18 ? * * *)" Description: "cron/rate expression by UTC" ReportName: Type: String Description: "" Description: "The scheduling expression that determines when and how often the rule runs" Resources: S3Bucket: Type: "AWS::S3::Bucket" Properties: BucketName: !Sub ${AWS::StackName}-${AWS::AccountId}-patch-report-bucket BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: "AES256" BucketKeyEnabled: false PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true LifecycleConfiguration: Rules: - Id: "auto-delete" Status: "Enabled" ExpirationInDays: 400 SNSTopic: Type: "AWS::SNS::Topic" Properties: DisplayName: !Sub ${AWS::StackName}-patch-report-topic Subscription: - Endpoint: !Ref EMailAddress Protocol: email TopicName: !Sub ${AWS::StackName}-patch-report-topic SNSTopicPolicy: Type: "AWS::SNS::TopicPolicy" Properties: Topics: - !Ref SNSTopic PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: AWS: "*" Action: - SNS:GetTopicAttributes - SNS:SetTopicAttributes - SNS:AddPermission - SNS:RemovePermission - SNS:DeleteTopic - SNS:Subscribe - SNS:ListSubscriptionsByTopic - SNS:Publish - SNS:Receive Resource: !Ref SNSTopic Condition: StringEquals: AWS:SourceOwner: !Ref AWS::AccountId EventsRule: Type: "AWS::Events::Rule" Properties: Name: !Sub AWS-SystemsManager-PatchManager-PatchReport-${AWS::StackName} Description: "Schedule recurring patch reporting" ScheduleExpression: !Ref ScheduleExpression State: "ENABLED" EventBusName: "default" Targets: - Id: !Sub ${AWS::StackName}-patch-report-target Arn: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/AWS-ExportPatchReportToS3:$DEFAULT" RoleArn: !GetAtt PatchAutomationRole.Arn Input: !Sub | { "assumeRole": [ "${PatchExportRole.Arn}" ], "reportName": [ "${ReportName}" ], "s3BucketName": [ "${S3Bucket}" ], "targets": [ "instanceids=*" ], "snsTopicArn": [ "${SNSTopic}" ] } PatchExportRole: Type: "AWS::IAM::Role" Properties: RoleName: !Sub ${AWS::StackName}-patch-summary-export-role AssumeRolePolicyDocument: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ssm.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}" MaxSessionDuration: 3600 ManagedPolicyArns: - !Ref PatchExportPolicy Description: "Service role for lambda to execute csv export of patch reports" PatchAutomationRole: Type: "AWS::IAM::Role" Properties: RoleName: !Sub ${AWS::StackName}-patch-automation-role AssumeRolePolicyDocument: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"events.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}" MaxSessionDuration: 3600 ManagedPolicyArns: - "arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole" - !Ref PatchAutomationPolicy Description: "Service role for event bridge to call ssm automation on a schedule" PatchAutomationPolicy: Type: "AWS::IAM::ManagedPolicy" Properties: ManagedPolicyName: !Sub ${AWS::StackName}-patch-automation-policy PolicyDocument: !Sub | { "Version": "2012-10-17", "Statement": [ { "Action": "ssm:StartAutomationExecution", "Effect": "Allow", "Resource": [ "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/AWS-ExportPatchReportToS3:$DEFAULT" ] }, { "Effect": "Allow", "Action": [ "iam:PassRole" ], "Resource": "${PatchExportRole.Arn}", "Condition": { "StringLikeIfExists": { "iam:PassedToService": "ssm.amazonaws.com" } } } ] } PatchExportPolicy: Type: "AWS::IAM::ManagedPolicy" Properties: ManagedPolicyName: !Sub ${AWS::StackName}-patch-summary-export-policy PolicyDocument: | { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject" ], "Resource": [ "*" ] }, { "Effect": "Allow", "Action": [ "s3:GetBucketAcl" ], "Resource": [ "*" ] }, { "Effect": "Allow", "Action": [ "sns:Publish" ], "Resource": [ "*" ] }, { "Effect": "Allow", "Action": [ "ssm:DescribeInstancePatchStates", "ssm:DescribeInstancePatches", "ssm:ListComplianceItems", "ssm:ListResourceComplianceSummaries", "ssm:DescribeInstanceInformation", "ssm:GetInventory", "ec2:DescribeInstances" ], "Resource": [ "*" ] } ] }
使い方
CloudFormationを実行します。
その後EC2インスタンスにタグを付与します。(キー:Patch Group)
TagEditorなど使うと複数インスタンスへの一括設定ができますので便利です。
注意点など
マネジメントコンソールからレポートのスケジュール機能を作成した場合、必要なロールが自動生成されますが、現時点ではどうやらポリシーが不足したロールが生成されるようでレポートのスケジュールを作成しただけだと定期レポートは動作しません。
よって今回はマネージドポリシーを追加しています。
さいごに
スモールスタートで運用しながらベースラインの見直しをすることを想定しているため、事前定義済みパッチは使いません。
これらの操作はすべてマネジメントコンソールから設定が可能ですが、スモールスタート用に流用しやすくしたかったのでCloudFormationテンプレートにしました。
テンプレートはGitHubリポジトリにも置いておきます。