Vuls で EC2 をバルスされないために(実践編)

これは Amazon EC2 Systems Manager Advent Calendar 2017 の -129 日目の記事です。

$ aws iam create-role --role-name EC2RoleSample --assume-role-policy-document '{"Version":"2012-10-17","Statement":{"Effect":"Allow","Principal":{"Service":"ec2.amazonaws.com"},"Action":"sts:AssumeRole"}}'
$ aws iam create-instance-profile --instance-profile-name EC2RoleSample
$ aws iam add-role-to-instance-profile --instance-profile-name EC2RoleSample --role-name EC2RoleSample
$ aws ec2 associate-iam-instance-profile --instance-id i-1234567890abcdef0 --iam-instance-profile Name=EC2RoleSample
    • EC2 インスタンスにアタッチされた IAM ロールに AmazonEC2RoleforSSM ポリシーをアタッチする
$ aws iam attach-role-policy --role-name EC2RoleSample --policy-arn arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM
[ec2-user ~]$ sudo yum install amazon-ssm-agent
[ec2-user ~]$ sudo start amazon-ssm-agent
    • Amazon EC2 Systems Manager は、なにかと便利なため、EC2 インスタンスの作成時にセットアップしておくことをおすすめします
  • 前提を満たしたら、やっていきます
$ terraform apply
var.vuls_roles
  Enter a value: []

var.vuls_users
  Enter a value: ["Bob"]

...
  • 晴れて IAM ユーザ "Bob" は EC2 インスタンス "i-1234567890abcdef0" に Vuls のためのユーザを作成できるようになりました
$ aws sts get-caller-identity --output text --query Arn
arn:aws:iam::123456789012:user/Bob
$ aws ssm send-command --document-name CreateVulsUser --instance-ids i-1234567890abcdef0 --parameters publickey="$(cat $HOME/.ssh/id_rsa.pub)" --output text --query Command.CommandId
854365f0-aefb-48eb-be4a-f2a0c6c2cf9e
  • 同時に実行結果として、インスタンスSSH ホスト公開鍵を取得できるので known_hosts に追加すれば安全に接続できます
$ aws ssm get-command-invocation --command-id 854365f0-aefb-48eb-be4a-f2a0c6c2cf9e --instance-id i-1234567890abcdef0 --output text --query StandardOutputContent | tee -a $HOME/.ssh/known_hosts
203.0.113.1 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJKQ5+Aauvvb5iPFfYeQhcIegsich7SJ1Ji97mh3sGx3wvXAV53wuzn4ILSEn9ENtb6bXT/puLiCUi2bza2To24= root@ip-172-31-28-103
$ ssh -t vuls@203.0.113.1 'stty cols 1000; curl --max-time 1 --retry 3 --noproxy 169.254.169.254 http://169.254.169.254/latest/meta-data/instance-id'
i-1234567890abcdef0Connection to 203.0.113.1 closed.

Vuls で EC2 をバルスされないために(簡易版)

$ sudo iptables -I OUTPUT -d 169.254.169.254 -j REJECT --reject-with icmp-admin-prohibited
  • 禁止されていることを確認
$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/role_name
curl: (7) Failed to connect to 169.254.169.254 port 80: ホストへの経路がありません
  • root ユーザ (UID 0) による通信を許可するルールを追加
$ sudo iptables -I OUTPUT -d 169.254.169.254 -m owner --uid-owner 0 -j ACCEPT
$ sudo curl http://169.254.169.254/latest/meta-data/iam/security-credentials/role_name
{
  ...
}
  • ec2-user ユーザ (UID 500) も許可します
$ sudo iptables -I OUTPUT -d 169.254.169.254 -m owner --uid-owner 500 -j ACCEPT
  • sudo なしで取れました
$ id
uid=500(ec2-user) gid=500(ec2-user) groups=500(ec2-user),10(wheel)
$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/role_name
{
  ...
}
  • 最終的なルールです
$ sudo iptables -L OUTPUT
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             instance-data.ap-northeast-1.compute.internal  owner UID match ec2-user
ACCEPT     all  --  anywhere             instance-data.ap-northeast-1.compute.internal  owner UID match root
REJECT     all  --  anywhere             instance-data.ap-northeast-1.compute.internal  reject-with icmp-admin-prohibited
[Jul  3 16:22:49] DEBUG [amazon] execResult: servername: amazon
  cmd: /usr/bin/ssh -tt -o StrictHostKeyChecking=yes -o LogLevel=quiet -o ConnectionAttempts=3 -o ConnectTimeout=10 -o ControlMaster=no -o ControlPath=none vuls-user@52.199.26.3 -p 22 -i /root/.ssh/id_rsa -o PasswordAuthentication=no stty cols 1000; curl --max-time 1 --retry 3 --noproxy 169.254.169.254 http://169.254.169.254/latest/meta-data/instance-id
  exitstatus: 0
  stdout: i-0bf0d78a268f80335
  stderr:
  err: %!s(<nil>)
[Jul  3 16:22:49]  INFO [localhost] (1/1) amazon is running on aws
[Jul  3 16:14:56] DEBUG [amazon] execResult: servername: amazon
  cmd: /usr/bin/ssh -tt -o StrictHostKeyChecking=yes -o LogLevel=quiet -o ConnectionAttempts=3 -o ConnectTimeout=10 -o ControlMaster=no -o ControlPath=none vuls-user@52.199.26.3 -p 22 -i /root/.ssh/id_rsa -o PasswordAuthentication=no stty cols 1000; curl --max-time 1 --retry 3 --noproxy 169.254.169.254 http://169.254.169.254/latest/meta-data/instance-id
  exitstatus: 7
  stdout: Warning: Transient problem: timeout Will retry in 1 seconds. 3 retries left.
curl: (7) Failed to connect to 169.254.169.254 port 80: No route to host

  stderr:
  err: %!s(<nil>)
[Jul  3 16:14:56]  INFO [localhost] (1/1) amazon is running on other

Vuls で EC2 をバルスされないために

  • 以下に Vuls の単純化した使用方法を示します
  • 対象の EC2 (Amazon Linux) に SSH ログインするための鍵ペアを作成
$ ssh-keygen -N '' -f $HOME/.ssh/id_rsa_vuls
$ cat $HOME/.ssh/id_rsa_vuls.pub
ssh-rsa ...
  • 対象の EC2 でアカウントを作成
ec2-user$ sudo adduser -m vuls
ec2-user$ sudo su vuls
vuls$ mkdir -m 700 /home/vuls/.ssh
vuls$ echo 'ssh-rsa ...' > /home/vuls/.ssh/authorized_keys
  • Vuls の設定ファイルを作成
$ mkdir vuls
$ cat <<EOD > vuls/config.toml
[servers]

[servers.amazon]
host        = "203.0.113.1"
port        = "22"
user        = "vuls"
keyPath     = "/root/.ssh/id_rsa_vuls"
EOD
  • Vuls のスキャンを Docker で実行
$ docker run --rm -it -v $HOME/.ssh:/root/.ssh:ro -v $PWD:/vuls -v $PWD/vuls-log:/var/log/vuls vuls/vuls scan
  • CVE をダウンロード(30分程度)
$ for i in `seq 2002 $(date +"%Y")`; do docker run --rm -it -v $PWD:/vuls -v $PWD/go-cve-dictionary-log:/var/log/vuls vuls/go-cve-dictionary fetchnvd -years $i; done
  • Vuls のレポートを表示
$ docker run --rm -it -v $HOME/.ssh:/root/.ssh:ro -v $PWD:/vuls -v $PWD/vuls-log:/var/log/vuls vuls/vuls report
  • これによって、ユーザが認証情報を管理する必要がなくなるため、アプリケーションサーバとしての EC2 に、様々な権限を持つロールをアタッチするケースが考えられます
ec2-user$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/role_name
  • ここで、前述の vuls アカウントに SSH ログインして、同じことが可能か試しましょう
$ ssh -t -i $HOME/.ssh/id_rsa_vuls vuls@203.0.113.1 'curl http://169.254.169.254/latest/meta-data/iam/security-credentials/role_name'
{
  "Code" : "Success",
  "LastUpdated" : "2017-06-27T09:17:33Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIAJHNLJH7SISLPSOMA",
  "SecretAccessKey" : "...",
  "Token" : "...",
  "Expiration" : "2017-06-27T15:39:38Z"
}Connection to 203.0.113.1 closed.
    • 取得できました
  • したがって、SSH 秘密鍵 "id_rsa_vuls" は、EC2 にアタッチされているロールによっては、高いレベルで保護する必要があります
  • しかし、その認識にギャップがあると思われ、次のように扱われる懸念があります
    • 脆弱性スキャンを自動実行させるため、秘密鍵パスフレーズを設定しない
    • 秘密鍵が置かれる、スキャン実行ホストのセキュリティレベルが低い、アクセス制御が徹底されていない
    • ロールによる権限を得られるべきではない外部のメンバー等が、スキャンを実行するために、秘密鍵へのアクセス権を持っている
  • vuls アカウントに設定することで、Vuls の実行のみが許可され、その他のコマンドは無視されます
vuls$ curl https://raw.githubusercontent.com/asannou/vuls-ssh-command/master/amazon-linux.sh > /home/vuls/.ssh/vuls-ssh-command.sh
vuls$ chmod +x /home/vuls/.ssh/vuls-ssh-command.sh
vuls$ echo 'command="/home/vuls/.ssh/vuls-ssh-command.sh" ssh-rsa ...' > /home/vuls/.ssh/authorized_keys
$ ssh -t -i $HOME/.ssh/id_rsa_vuls vuls@203.0.113.1 'curl http://169.254.169.254/latest/meta-data/iam/security-credentials/role_name'
Connection to 203.0.113.1 closed.
  • たぶんバグあり、Amazon LinuxUbuntu しか用意していませんが、よろしければお使いください