logo サブスレッド

EC2へのSSH接続を簡単にしたい! (4)自動書き換えスクリプト

株式会社サブスレッドのみずしま a.k.a mzsm(@mzsm_j)です。こんにちは。

今回も前回の続きになります。

今回までで、~/.ssh/configに必要な情報をプログラムから取得することができましたので、今回は~/.ssh/configを自動的に書き換えるプログラムを作ってみます。

~/.ssh/configって分割できるの?

「設定ファイルが長くなると管理が面倒になってくる」とか「デフォルトの設定ファイルはなるべくそのままにしておきたい」などの理由で設定ファイルを複数に分割し、大元でincludeして使うということはよくあります。

~/.ssh/configでもそのようなことができないのか調べてみましたが、どうやら分割には対応していないようです。
というわけで、しょうがないので~/.ssh/configを直接書き換えることになります。

どうやって書き換えようか

~/.ssh/configは手動で書き換えることもあるので、対象の部分だけを書き換え、それ以外の部分はそのまま残しておく必要があります。

今回は、特定の文字列で始まる行を「マーカー」として利用し、2つのマーカー行の間は更新時に書き換え、その外側はそのまま残すという手法を取ることにしました。

### BEGIN_AWS ###を書き換え開始行、### END_AWS ###を書き換え終了行のマーカーとします。
この2行を間を空けずに続けて追記しておきます。

~/.ssh/configはこのようになりました。

# 私の./ssh/config
# このあたりは元々あった設定
Host hogehoge
    HostName example.jp

### BEGIN_AWS ###
### END_AWS ###

# このあたりも元々あった設定
Host fugafuga
    HostName example.com

プログラムは以下のようになります。

# -*- coding: utf-8 -*-
import os
try:
    from io import StringIO  # py3
except ImportError:
    from cStringIO import StringIO  # py2

import boto3
import six


# 取得対象アカウント
ACCOUNTS = [
    ('AKIAXXXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', 'ap-northeast-1', 'eigyo-kaizen-'),
    ('AKIAYYYYYYYYYYYYYYYY', 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY', 'ap-northeast-1', 'some-project-'),
]

def main():    
    config_line = []
    # インスタンス情報の取得
    for access_key_id, secret_access_key, region, host_prefix in ACCOUNTS:
        session = boto3.Session(aws_access_key_id=access_key_id,
                                aws_secret_access_key=secret_access_key,
                                region_name=region)
        client = session.client('ec2')
        # 起動中のインスタンス一覧を取得
        filters = [
            {'Name': 'instance-state-name', 'Values': ['running']}
        ]
        response = client.describe_instances(Filters=filters)
        for reservation in response['Reservations']:
            for instance in reservation['Instances']:
                tags = {tag['Key']: tag['Value'] for tag in instance['Tags']}
                config_line.append(u'Host ' + host_prefix + tags.get('HostName'))
                config_line.append(u'    HostName ' + instance['PublicDnsName'])
        config_line.append(u'')

    # ~/.ssh/configの書き換え
    file_path = os.path.expanduser(os.path.join('~', '.ssh', 'config'))
    with open(file_path, 'r+') as ssh_config:
        io = StringIO()
        skip = False
        for line in ssh_config:
            if line.startswith('### END_AWS ###'):
                # 終了行マーカーが現れたらスキップ対象行終了
                skip = False

            if not skip:
                # スキップ対象行以外(マーカーの外側)は、現在のファイルの内容をそのまま利用する
                if six.PY2:
                    line = unicode(line)
                io.write(line)

            if line.startswith('### BEGIN_AWS ###'):
                # 開始行マーカーが現れたらスキップ対象行開始
                io.writelines([x + '\n' for x in config_line])
                # 開始行マーカーが複数回出現しても1回しか書き込まないように、内容を空にしておく
                config_line = []
                skip = True

        ssh_config.seek(0)    # ファイル先頭にシーク
        ssh_config.truncate(0)    # ファイル内容を全削除
        ssh_config.write(io.getvalue())    # StringIOに溜めていた内容を書き込む

if __name__ == '__main__':
    main()

このプログラムを実行すると、./ssh/configが次のように書き換わります。

# 私の./ssh/config
# このあたりは元々あった設定
Host hogehoge
    HostName example.jp

### BEGIN_AWS ###
Host eigyo-kaizen-prod-1
    HostName  ec2-203-0-113-23.ap-northeast-1.compute.amazonaws.com
Host eigyo-kaizen-test-2
    HostName  ec2-203-0-113-179.ap-northeast-1.compute.amazonaws.com
Host eigyo-kaizen-test-1
    HostName  ec2-203-0-113-10.ap-northeast-1.compute.amazonaws.com
Host eigyo-kaizen-prod-2
    HostName  ec2-203-0-113-205.ap-northeast-1.compute.amazonaws.com
Host eigyo-kaizen-dev-1
    HostName  ec2-203-0-113-200.ap-northeast-1.compute.amazonaws.com

Host some-project-test-1
    HostName  ec2-203-0-113-45.ap-northeast-1.compute.amazonaws.com
Host some-project-test-2
    HostName  ec2-203-0-113-164.ap-northeast-1.compute.amazonaws.com

### END_AWS ###

# このあたりも元々あった設定
Host fugafuga
    HostName example.com

これでIPアドレスがどれだけ変わっても常に同じホスト名でアクセスできる環境を維持できますね。

さいごに

あとはこのプログラムをもとに、取得対象アカウントの情報を外に出したり、決め打ちになっている対象タグ名を変更できるようにするなどしていけばよいでしょう。

初週から連載してきた連載は今回でとりあえずいったんお休みにします。

次回からは、小ネタをちびちび紹介していこうかと思っています。

現在の位置:サブスレッド ホーム > 技術ブログ > EC2へのSSH接続を簡単にしたい! (4)自動書き換えスクリプト