LoginSignup
16
8

More than 5 years have passed since last update.

EC2にキーペアが配信されるしくみ

Last updated at Posted at 2016-12-14

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-initInstance metadata からSSH公開鍵を取ってきて、 ~/.ssh/authorized_keys を更新している様です。

  • /usr/lib/python2.7/dist-packages/cloudinit/ssh_util.py

これはAmazonが独自にカスタマイズしたPythonスクリプトで、コードを見た感じ以下の動きをします。

  1. Instance metadata から AWS Console >> EC2 >> Key Pairs に登録したSSH公開鍵を取ってくる
  2. ~/.ssh/authorized_keys をラインパースする
  3. 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)
16
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
8