AWS ConsoleからEC2を起動すると、指定したキーペアが知らないあいだに配信されてて、手元の秘密鍵でSSH接続できます。
どういう仕組なのか知らなかったのでちょっと調べました。
検証に利用した Amazon Linux AMI:
amzn-ami-hvm-2015.09.1.x86_64-gp2 (ami-383c1956)
SSH公開鍵 -> ~/.ssh/authorized_keys
SSHの公開鍵はここにあります。
[ec2-user]$ cat ~/.ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2E.... {キーペア名}
EC2が起動した時には既にあるわけですが、 (じゃないとSSH接続できない) どうやって配信?されているのでしょうか。
Instance metadata からSSH公開鍵を取得できる
AWSでは Instance metadata と呼ばれるEC2インスタンス個別のデータがあります。これは HTTP GET でEC2インスタンスから参照できます。
例えば、SSH公開鍵はこうやって取れます。
[ec2-user]$ curl -s -L http://169.254.169.254/2011-01-01/meta-data/public-keys/0/openssh-key/
ssh-rsa AAAAB3NzaC1yc2E.... {キーペア名}
起動時に cloudinit が ~/.ssh/authorized_keys を更新
EC2インスタンスの起動時に cloud-init が実行されています。
cloud-init が Instance metadata からSSH公開鍵を取ってきて、 ~/.ssh/authorized_keys
を更新している様です。
/usr/lib/python2.7/dist-packages/cloudinit/ssh_util.py
これはAmazonが独自にカスタマイズしたPythonスクリプトで、コードを見た感じ以下の動きをします。
- Instance metadata から AWS Console >> EC2 >> Key Pairs に登録したSSH公開鍵を取ってくる
-
~/.ssh/authorized_keys
をラインパースする -
Instance metadata から取って来た公開鍵とBASE64部分が一致するラインがあるか...?
- (ある) ==> BASE64部分が一致するラインを更新する
- (ない) ==> 一番下に新規に追加する
/usr/lib/python2.7/dist-packages/cloudinit/ssh_util.py
def update_authorized_keys(old_entries, keys):
to_add = list(keys)
for i in range(0, len(old_entries)):
ent = old_entries[i]
if not ent.valid():
continue
# Replace those with the same base64
for k in keys:
if not ent.valid():
continue
if k.base64 == ent.base64:
# Replace it with our better one
ent = k
# Don't add it later
if k in to_add:
to_add.remove(k)
old_entries[i] = ent
# Now append any entries we did not match above
for key in to_add:
old_entries.append(key)
# Now format them back to strings...
lines = [str(b) for b in old_entries]
# Ensure it ends with a newline
lines.append('')
return '\n'.join(lines)