SSM SessionManagerのログをS3 bucket/CloudWatchLogに保存する

SSM SessionManagerでのコンソールの操作ログをS3やCloudWatchLogsに保存できるということなので試してみた。Terraformで設定。

1. IAMrole / Instance Profileを作成

ec2インスタンスにインストールされているSSMエージェントがログを書き込むので、インスタンスに権限を付与してあげる必要がある。こちらのドキュメントを参考に設定。今回は AmazonSSMManagedInstanceCore に権限を追加してみた。Terraformのコードはこんな感じ。

data "aws_iam_policy" "amazon_ssm_managed_instance_core" {
    arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

data "aws_iam_policy_document" "ec2_ssm_test_policy_doc" {
    source_json = data.aws_iam_policy.amazon_ssm_managed_instance_core.policy

    statement {
        effect    = "Allow"
        resources = ["*"]

        actions = [
            "s3:PutObject",
            "s3:GetEncryptionConfiguration",
            "logs:PutLogEvents",
            "logs:CreateLogStream",
            "logs:DescribeLogGroups",
            "logs:DescribeLogStreams",
            "ssm:GetParameter",
            "ssm:GetParameters",
            "ssm:GetParametersByPath",
            "kms:Decrypt",
            "kms:GenerateDataKey",
        ]
    }
}

resource "aws_iam_policy" "ec2_ssm_test_policy" {
    name = "ec2-ssm-test-policy"
    policy = data.aws_iam_policy_document.ec2_ssm_test_policy_doc.json
}

resource "aws_iam_role" "ec2_ssm_test_role" {
    name = "ec2-ssm-test-role"

    assume_role_policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "sts:AssumeRole",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Effect": "Allow",
            "Sid": ""
        }
    ]
}
EOF

    tags = {
        Name = "ec2-ssm-test-role"
    }
}


resource "aws_iam_role_policy_attachment" "ssm_role_attachment" {
    role       = aws_iam_role.ec2_ssm_test_role.name
    policy_arn = aws_iam_policy.ec2_ssm_test_policy.arn
}

resource "aws_iam_instance_profile" "ec2_ssm_test_profile" {
    name = "ec2-ssm-test-profile"
    role = aws_iam_role.ec2_ssm_test_role.name
}

2. 作成したinstance profileをアタッチしたec2インスタンスを作成

amazon linux2を利用*1。Terraformのコードはこんな感じ。VPCとかSubnet、SecurityGroupは適宜設定。

data "aws_ssm_parameter" "amzn2_ami" {
    name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
}

resource "aws_instance" "ssm_test_server" {
    ami                  = data.aws_ssm_parameter.amzn2_ami.value
    instance_type        = "t2.micro"
    subnet_id            = aws_subnet.ec2_ssm_test_private_subnet.id
    iam_instance_profile = aws_iam_instance_profile.ec2_ssm_test_profile.name

    security_groups = [
        aws_security_group.allow_http.id,
    ]

    tags = {
        Name = "ssm-test-server"
    }

    user_data = <<EOF
#!/bin/bash
yum update -y
amazon-linux-extras install docker
usermod -aG docker ec2-user
systemctl enable docker
systemctl start docker
EOF
}

3. SSM SessionManager設定用Documentの作成

SSM SessionManagerの設定はSSM Documentで作成するようなので、terraformで作成。今回はテストなので暗号化はしない設定。ログ保存用のS3 bucketとCloudWatchLogsのLogGroupも一緒に作成。因みにDocumentの名前は "SSM-SessionManagerRunShell"にしておくとよい。session mamager使う時にデフォルトで読み込んでくれる。勿論名前変えて、CLIの引数で渡して指定することも可能*2

resource "aws_s3_bucket" "ec2_ssm_test_log_bucket" {
    bucket        = "ec2-ssm-test-log-bucket-xxx"
    force_destroy = true

    lifecycle_rule {
        enabled = true

        expiration {
            days = 3
        }
    }
}

resource "aws_cloudwatch_log_group" "ec2_ssm_test_log" {
    name              = "/ec2-ssm-test-log"
    retention_in_days = 3
}

resource "aws_ssm_document" "session_manager_run_shell" {
    name            = "SSM-SessionManagerRunShell"
    document_type   = "Session"
    document_format = "JSON"

    content = <<EOF
{
    "schemaVersion": "1.0",
    "description": "Document to hold regional settings for Session Manager",
    "sessionType": "Standard_Stream",
    "inputs": {
        "s3BucketName": "${aws_s3_bucket.ec2_ssm_test_log_bucket.id}",
        "s3EncryptionEnabled": false,
        "cloudWatchLogGroupName": "${aws_cloudwatch_log_group.ec2_ssm_test_log.name}",
        "cloudWatchEncryptionEnabled": false
    }
}
EOF
}

いざapplyと思って実行するとエラー。SSM-SessionManagerRunShell が存在するとのこと。どうもSSMを使ったことがあると自動で作成されている模様。消す*3かimportすればよさそう。今回はimportしちゃった。

$ terraform import --target aws_ssm_document.session_manager_run_shell SSH-SessionManagerRunShell
aws_ssm_document.session_manager_run_shell: Importing from ID "SSM-SessionManagerRunShell"...
aws_ssm_document.session_manager_run_shell: Import prepared!
  Prepared aws_ssm_document for import
aws_ssm_document.session_manager_run_shell: Refreshing state... [id=SSM-SessionManagerRunShell]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

無事importできた。その後applyすると無事適用できました。

$ terraform apply
...

4. 確認

  • ログはSessionを終了すると保存される模様
  • CloudWatch、S3ともに保存できてました。CloudWatchLogsだとこんな感じ。文字化けしてるけど、コンソールで実行したコマンド、その結果ちゃんと保存できてる。
Script started on 2020-10-18 13:32:11+0000
[?1034hsh-4.2$ 
[Ksh-4.2$ bash
]0;@ip-10-0-1-xxx:/usr/bin[?1034h[ssm-user@ip-10-0-1-xxx bin]$ sudo su - ec2-use
]0;ec2-user@ip-10-0-1-xxx:~[ec2-user@ip-10-0-1-xxx ~]$ uname -a
Linux ip-10-0-1-xxx.ap-northeast-1.compute.internal 4.14.193-149.317.amzn2.x86_64 #1 SMP Thu Sep 3 19:04:44 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
]0;ec2-user@ip-10-0-1-xxx:~[ec2-user@ip-10-0-1-xxx ~]$ hostname
ip-10-0-1-xxx.ap-northeast-1.compute.internal
]0;ec2-user@ip-10-0-1-xxx:~[ec2-user@ip-10-0-1-xxx ~]$ uptime
 13:26:27 up 5 min,  0 users,  load average: 0.02, 0.12, 0.08
]0;ec2-user@ip-10-0-1-xxx:~[ec2-user@ip-10-0-1-xxx ~]$ whoami
ec2-user
]0;ec2-user@ip-10-0-1-xxx:~[ec2-user@ip-10-0-1-xxx ~]$ exit
logout
]0;@ip-10-0-1-xxx:/usr/bin[ssm-user@ip-10-0-1-xxx bin]$ exit
exit
sh-4.2$ exit
exit

Script done on 2020-10-18 13:32:11+0000

*1:ami idはSSM parameter storeから取得。参考

*2:試してないけど

*3:AWS consoleからの削除方法不明。CLI使えば消せそう